aboutsummaryrefslogtreecommitdiffstats
path: root/src/vnet/session/session_api.c
blob: a729baaae2952586210b036645219acf3aa46710 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
<
#!/usr/bin/env python
"""IP6 VRF Multi-instance Test Case HLD:

**NOTES:**
    - higher number of pg-ip6 interfaces causes problems => only 15 pg-ip6 \
    interfaces in 5 VRFs are tested
    - jumbo packets in configuration with 15 pg-ip6 interfaces leads to \
    problems too

**config 1**
    - add 15 pg-ip6 interfaces
    - configure 5 hosts per pg-ip6 interface
    - configure 4 VRFs
    - add 3 pg-ip6 interfaces per VRF

**test 1**
    - send IP6 packets between all pg-ip6 interfaces in all VRF groups

**verify 1**
    - check VRF data by parsing output of ip6_fib_dump API command
    - all packets received correctly in case of pg-ip6 interfaces in the same
    VRF
    - no packet received in case of pg-ip6 interfaces not in VRF
    - no packet received in case of pg-ip6 interfaces in different VRFs

**config 2**
    - reset 2 VRFs

**test 2**
    - send IP6 packets between all pg-ip6 interfaces in all VRF groups

**verify 2**
    - check VRF data by parsing output of ip6_fib_dump API command
    - all packets received correctly in case of pg-ip6 interfaces in the same
    VRF
    - no packet received in case of pg-ip6 interfaces not in VRF
    - no packet received in case of pg-ip6 interfaces in different VRFs

**config 3**
    - add 1 of reset VRFs and 1 new VRF

**test 3**
    - send IP6 packets between all pg-ip6 interfaces in all VRF groups

**verify 3**
    - check VRF data by parsing output of ip6_fib_dump API command
    - all packets received correctly in case of pg-ip6 interfaces in the same
    VRF
    - no packet received in case of pg-ip6 interfaces not in VRF
    - no packet received in case of pg-ip6 interfaces in different VRFs

**config 4**
    - reset all VRFs (i.e. no VRF except VRF=0 created)

**test 4**
    - send IP6 packets between all pg-ip6 interfaces in all VRF groups

**verify 4**
    - check VRF data by parsing output of ip6_fib_dump API command
    - all packets received correctly in case of pg-ip6 interfaces in the same
    VRF
    - no packet received in case of pg-ip6 interfaces not in VRF
    - no packet received in case of pg-ip6 interfaces in different VRFs
"""

import unittest
import random
import socket

from scapy.packet import Raw
from scapy.layers.l2 import Ether
from scapy.layers.inet6 import UDP, IPv6, ICMPv6ND_NS, ICMPv6ND_RA, \
    RouterAlert, IPv6ExtHdrHopByHop
from scapy.utils6 import in6_ismaddr, in6_isllsnmaddr, in6_getAddrType
from scapy.pton_ntop import inet_ntop

from framework import VppTestCase, VppTestRunner
from util import ppp
from vrf import VRFState


def is_ipv6_misc_ext(p):
    """ Is packet one of uninteresting IPv6 broadcasts (extended to filter out
    ICMPv6 Neighbor Discovery - Neighbor Advertisement packets too)? """
    if p.haslayer(ICMPv6ND_RA):
        if in6_ismaddr(p[IPv6].dst):
            return True
    if p.haslayer(ICMPv6ND_NS):
        if in6_isllsnmaddr(p[IPv6].dst):
            return True
    if p.haslayer(IPv6ExtHdrHopByHop):
        for o in p[IPv6ExtHdrHopByHop].options:
            if isinstance(o, RouterAlert):
                return True
    return False


class TestIP6VrfMultiInst(VppTestCase):
    """ IP6 VRF  Multi-instance Test Case """

    @classmethod
    def setUpClass(cls):
        """
        Perform standard class setup (defined by class method setUpClass in
        class VppTestCase) before running the test case, set test case related
        variables and configure VPP.
        """
        super(TestIP6VrfMultiInst, cls).setUpClass()

        # Test variables
        cls.hosts_per_pg = 5
        cls.nr_of_vrfs = 5
        cls.pg_ifs_per_vrf = 3

        try:
            # Create pg interfaces
            cls.create_pg_interfaces(
                range(cls.nr_of_vrfs * cls.pg_ifs_per_vrf))

            # Packet flows mapping pg0 -> pg1, pg2 etc.
            cls.flows = dict()
            for i in range(len(cls.pg_interfaces)):
                multiplicand = i / cls.pg_ifs_per_vrf
                pg_list = [
                    cls.pg_interfaces[multiplicand * cls.pg_ifs_per_vrf + j]
                    for j in range(cls.pg_ifs_per_vrf)
                    if (multiplicand * cls.pg_ifs_per_vrf + j) != i]
                cls.flows[cls.pg_interfaces[i]] = pg_list

            # Packet sizes - jumbo packet (9018 bytes) skipped
            cls.pg_if_packet_sizes = [64, 512, 1518]

            # Set up all interfaces
            for pg_if in cls.pg_interfaces:
                pg_if.admin_up()
                pg_if.generate_remote_hosts(cls.hosts_per_pg)

            # Create list of VRFs
            cls.vrf_list = list()

            # Create list of reset VRFs
            cls.vrf_reset_list = list()

            # Create list of pg_interfaces in VRFs
            cls.pg_in_vrf = list()

            # Create list of pg_interfaces not in VRFs
            cls.pg_not_in_vrf = [pg_if for pg_if in cls.pg_interfaces]

            # Create mapping of pg_interfaces to VRF IDs
            cls.pg_if_by_vrf_id = dict()
            for i in range(cls.nr_of_vrfs):
                vrf_id = i + 1
                pg_list = [
                    cls.pg_interfaces[i * cls.pg_ifs_per_vrf + j]
                    for j in range(cls.pg_ifs_per_vrf)]
                cls.pg_if_by_vrf_id[vrf_id] = pg_list

        except Exception:
            super(TestIP6VrfMultiInst, cls).tearDownClass()
            raise

    def setUp(self):
        """
        Clear trace and packet infos before running each test.
        """
        super(TestIP6VrfMultiInst, self).setUp()
        self.reset_packet_infos()

    def tearDown(self):
        """
        Show various debug prints after each test.
        """
        super(TestIP6VrfMultiInst, self).tearDown()
        if not self.vpp_dead:
            self.logger.info(self.vapi.ppcli("show ip6 fib"))
            self.logger.info(self.vapi.ppcli("show ip6 neighbors"))

    def create_vrf_and_assign_interfaces(self, count, start=1):
        """
        Create required number of FIB tables / VRFs, put 3 pg-ip6 interfaces
        to every FIB table / VRF.

        :param int count: Number of FIB tables / VRFs to be created.
        :param int start: Starting number of the FIB table / VRF ID. \
        (Default value = 1)
        """
        for i in range(count):
            vrf_id = i + start
            pg_if = self.pg_if_by_vrf_id[vrf_id][0]
            dest_addr = pg_if.local_ip6n
            dest_addr_len = 64
            self.vapi.ip_table_add_del(vrf_id, is_add=1, is_ipv6=1)
            self.logger.info("IPv6 VRF ID %d created" % vrf_id)
            if vrf_id not in self.vrf_list:
                self.vrf_list.append(vrf_id)
            if vrf_id in self.vrf_reset_list:
                self.vrf_reset_list.remove(vrf_id)
            for j in range(self.pg_ifs_per_vrf):
                pg_if = self.pg_if_by_vrf_id[vrf_id][j]
                pg_if.set_table_ip6(vrf_id)
                self.logger.info("pg-interface %s added to IPv6 VRF ID %d"
                                 % (pg_if.name, vrf_id))
                if pg_if not in self.pg_in_vrf:
                    self.pg_in_vrf.append(pg_if)
                if pg_if in self.pg_not_in_vrf:
                    self.pg_not_in_vrf.remove(pg_if)
                pg_if.config_ip6()
                pg_if.disable_ipv6_ra()
                pg_if.configure_ipv6_neighbors()
        self.logger.debug(self.vapi.ppcli("show ip6 fib"))
        self.logger.debug(self.vapi.ppcli("show ip6 neighbors"))

    def reset_vrf_and_remove_from_vrf_list(self, vrf_id):
        """
        Reset required FIB table / VRF and remove it from VRF list.

        :param int vrf_id: The FIB table / VRF ID to be reset.
        """
        # self.vapi.reset_vrf(vrf_id, is_ipv6=1)
        self.vapi.reset_fib(vrf_id, is_ipv6=1)
        if vrf_id in self.vrf_list:
            self.vrf_list.remove(vrf_id)
        if vrf_id not in self.vrf_reset_list:
            self.vrf_reset_list.append(vrf_id)
        for j in range(self.pg_ifs_per_vrf):
            pg_if = self.pg_if_by_vrf_id[vrf_id][j]
            pg_if.unconfig_ip6()
            if pg_if in self.pg_in_vrf:
                self.pg_in_vrf.remove(pg_if)
            if pg_if not in self.pg_not_in_vrf:
                self.pg_not_in_vrf.append(pg_if)
        self.logger.info("IPv6 VRF ID %d reset finished" % vrf_id)
        self.logger.debug(self.vapi.ppcli("show ip6 fib"))
        self.logger.debug(self.vapi.ppcli("show ip6 neighbors"))
        self.vapi.ip_table_add_del(vrf_id, is_add=0, is_ipv6=1)

    def create_stream(self, src_if, packet_sizes):
        """
        Create input packet stream for defined interface using hosts list.

        :param object src_if: Interface to create packet stream for.
        :param list packet_sizes: List of required packet sizes.
        :return: Stream of packets.
        """
        pkts = []
        src_hosts = src_if.remote_hosts
        for dst_if in self.flows[src_if]:
            for dst_host in dst_if.remote_hosts:
                src_host = random.choice(src_hosts)
                pkt_info = self.create_packet_info(src_if, dst_if)
                payload = self.info_to_payload(pkt_info)
                p = (Ether(dst=src_if.local_mac, src=src_host.mac) /
                     IPv6(src=src_host.ip6, dst=dst_host.ip6) /
                     UDP(sport=1234, dport=1234) /
                     Raw(payload))
                pkt_info.data = p.copy()
                size = random.choice(packet_sizes)
                self.extend_packet(p, size)
                pkts.append(p)
        self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
                          % (src_if.name, len(pkts)))
        return pkts

    def create_stream_crosswise_vrf(self, src_if, vrf_id, packet_sizes):
        """
        Create input packet stream for negative test for leaking across
        different VRFs for defined interface using hosts list.

        :param object src_if: Interface to create packet stream for.
        :param int vrf_id: The FIB table / VRF ID where src_if is assigned.
        :param list packet_sizes: List of required packet sizes.
        :return: Stream of packets.
        """
        pkts = []
        src_hosts = src_if.remote_hosts
        vrf_lst = list(self.vrf_list)
        vrf_lst.remove(vrf_id)
        for vrf in vrf_lst:
            for dst_if in self.pg_if_by_vrf_id[vrf]:
                for dst_host in dst_if.remote_hosts:
                    src_host = random.choice(src_hosts)
                    pkt_info = self.create_packet_info(src_if, dst_if)
                    payload = self.info_to_payload(pkt_info)
                    p = (Ether(dst=src_if.local_mac, src=src_host.mac) /
                         IPv6(src=src_host.ip6, dst=dst_host.ip6) /
                         UDP(sport=1234, dport=1234) /
                         Raw(payload))
                    pkt_info.data = p.copy()
                    size = random.choice(packet_sizes)
                    self.extend_packet(p, size)
                    pkts.append(p)
        self.logger.debug("Input stream created for port %s. Length: %u pkt(s)"
                          % (src_if.name, len(pkts)))
        return pkts

    def verify_capture(self, pg_if, capture):
        """
        Verify captured input packet stream for defined interface.

        :param object pg_if: Interface to verify captured packet stream for.
        :param list capture: Captured packet stream.
        """
        last_info = dict()
        for i in self.pg_interfaces:
            last_info[i.sw_if_index] = None
        dst_sw_if_index = pg_if.sw_if_index
        for packet in capture:
            try:
                ip = packet[IPv6]
                udp = packet[UDP]
                payload_info = self.payload_to_info(str(packet[Raw]))
                packet_index = payload_info.index
                self.assertEqual(payload_info.dst, dst_sw_if_index)
                self.logger.debug("Got packet on port %s: src=%u (id=%u)" %
                                  (pg_if.name, payload_info.src, packet_index))
                next_info = self.get_next_packet_info_for_interface2(
                    payload_info.src, dst_sw_if_index,
                    last_info[payload_info.src])
                last_info[payload_info.src] = next_info
                self.assertIsNotNone(next_info)
                self.assertEqual(packet_index, next_info.index)
                saved_packet = next_info.data
                # Check standard fields
                self.assertEqual(ip.src, saved_packet[IPv6].src)
                self.assertEqual(ip.dst, saved_packet[IPv6].dst)
                self.assertEqual(udp.sport, saved_packet[UDP].sport)
                self.assertEqual(udp.dport
@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highlight .kd { color: #66d9ef } /* Keyword.Declaration */
.highlight .kn { color: #f92672 } /* Keyword.Namespace */
.highlight .kp { color: #66d9ef } /* Keyword.Pseudo */
.highlight .kr { color: #66d9ef } /* Keyword.Reserved */
.highlight .kt { color: #66d9ef } /* Keyword.Type */
.highlight .ld { color: #e6db74 } /* Literal.Date */
.highlight .m { color: #ae81ff } /* Literal.Number */
.highlight .s { color: #e6db74 } /* Literal.String */
.highlight .na { color: #a6e22e } /* Name.Attribute */
.highlight .nb { color: #f8f8f2 } /* Name.Builtin */
.highlight .nc { color: #a6e22e } /* Name.Class */
.highlight .no { color: #66d9ef } /* Name.Constant */
.highlight .nd { color: #a6e22e } /* Name.Decorator */
.highlight .ni { color: #f8f8f2 } /* Name.Entity */
.highlight .ne { color: #a6e22e } /* Name.Exception */
.highlight .nf { color: #a6e22e } /* Name.Function */
.highlight .nl { color: #f8f8f2 } /* Name.Label */
.highlight .nn { color: #f8f8f2 } /* Name.Namespace */
.highlight .nx { color: #a6e22e } /* Name.Other */
.highlight .py { color: #f8f8f2 } /* Name.Property */
.highlight .nt { color: #f92672 } /* Name.Tag */
.highlight .nv { color: #f8f8f2 } /* Name.Variable */
.highlight .ow { color: #f92672 } /* Operator.Word */
.highlight .w { color: #f8f8f2 } /* Text.Whitespace */
.highlight .mb { color: #ae81ff } /* Literal.Number.Bin */
.highlight .mf { color: #ae81ff } /* Literal.Number.Float */
.highlight .mh { color: #ae81ff } /* Literal.Number.Hex */
.highlight .mi { color: #ae81ff } /* Literal.Number.Integer */
.highlight .mo { color: #ae81ff } /* Literal.Number.Oct */
.highlight .sa { color: #e6db74 } /* Literal.String.Affix */
.highlight .sb { color: #e6db74 } /* Literal.String.Backtick */
.highlight .sc { color: #e6db74 } /* Literal.String.Char */
.highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */
.highlight .sd { color: #e6db74 } /* Literal.String.Doc */
.highlight .s2 { color: #e6db74 } /* Literal.String.Double */
.highlight .se { color: #ae81ff } /* Literal.String.Escape */
.highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */
.highlight .si { color: #e6db74 } /* Literal.String.Interpol */
.highlight .sx { color: #e6db74 } /* Literal.String.Other */
.highlight .sr { color: #e6db74 } /* Literal.String.Regex */
.highlight .s1 { color: #e6db74 } /* Literal.String.Single */
.highlight .ss { color: #e6db74 } /* Literal.String.Symbol */
.highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #a6e22e } /* Name.Function.Magic */
.highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */
.highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */
.highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}
@media (prefers-color-scheme: light) {
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
}
/*
 * Copyright (c) 2015-2019 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <vnet/vnet.h>
#include <vlibmemory/api.h>
#include <vnet/session/application.h>
#include <vnet/session/application_interface.h>
#include <vnet/session/application_local.h>
#include <vnet/session/session.h>
#include <vnet/session/session_table.h>
#include <vnet/session/session_rules_table.h>
#include <vnet/session/session_sdl.h>
#include <vnet/ip/ip_types_api.h>

#include <vnet/format_fns.h>
#include <vnet/session/session.api_enum.h>
#include <vnet/session/session.api_types.h>

#define REPLY_MSG_ID_BASE session_main.msg_id_base
#include <vlibapi/api_helper_macros.h>

VLIB_REGISTER_LOG_CLASS (session_api_log, static) = { .class_name = "session",
						      .subclass_name = "api" };

#define log_debug(fmt, ...)                                                   \
  vlib_log_debug (session_api_log.class, "%s: " fmt, __func__, __VA_ARGS__)
#define log_warn(fmt, ...)                                                    \
  vlib_log_warn (session_api_log.class, fmt, __VA_ARGS__)
#define log_err(fmt, ...)                                                     \
  vlib_log_err (session_api_log.class, fmt, __VA_ARGS__)

static int
verify_message_len (void *mp, u64 expected_len, char *where)
{
  u32 supplied_len = vl_msg_api_get_msg_length (mp);

  if (supplied_len < expected_len)
    {
      log_err ("%s: Supplied message length %d is less than expected %d",
	       where, supplied_len, expected_len);
      return 0;
    }
  else
    {
      return 1;
    }
}

static void
vl_api_session_sdl_add_del_t_handler (vl_api_session_sdl_add_del_t *mp)
{
  vl_api_session_sdl_add_del_reply_t *rmp;
  session_rule_add_del_args_t args;
  session_rule_table_add_del_args_t *table_args = &args.table_args;
  int rv = 0;
  u32 count = clib_net_to_host_u32 (mp->count);
  u64 expected_len = sizeof (*mp) + count * sizeof (mp->r[0]);

  if ((session_main.is_enabled == 0) || (session_sdl_is_enabled () == 0))
    {
      rv = VNET_API_ERROR_FEATURE_DISABLED;
      goto done;
    }

  if (!verify_message_len (mp, expected_len, "session_sdl_add_del"))
    {
      rv = VNET_API_ERROR_INVALID_VALUE;
      goto done;
    }

  clib_memset (&args, 0, sizeof (args));
  table_args->is_add = mp->is_add;
  args.scope = SESSION_RULE_SCOPE_GLOBAL;
  args.appns_index = clib_net_to_host_u32 (mp->appns_index);
  for (int i = 0; i < count; i++)
    {
      mp->r[i].tag[sizeof (mp->r[i].tag) - 1] = 0;
      table_args->tag = format (0, "%s", mp->r[i].tag);
      ip_prefix_decode (&mp->r[i].lcl, &table_args->lcl);
      /*
       * Need to set fp_proto for vnet_session_rule_add_del to find the
       * correct table
       */
      table_args->rmt.fp_proto = table_args->lcl.fp_proto;
      table_args->action_index = clib_net_to_host_u32 (mp->r[i].action_index);

      rv = vnet_session_rule_add_del (&args);
      vec_free (table_args->tag);
      if (rv)
	{
	  log_err ("session_sdl add del returned on %U @index %d: %U",
		   format_ip46_address, &table_args->lcl.fp_addr,
		   IP46_TYPE_ANY, i, format_session_error, rv);

	  /* roll back */
	  table_args->is_add = !mp->is_add;
	  for (int j = i - 1; j >= 0; j--)
	    {
	      mp->r[j].tag[sizeof (mp->r[j].tag) - 1] = 0;
	      table_args->tag = format (0, "%s", mp->r[j].tag);
	      ip_prefix_decode (&mp->r[j].lcl, &table_args->lcl);
	      table_args->rmt.fp_proto = table_args->lcl.fp_proto;
	      table_args->action_index =
		clib_net_to_host_u32 (mp->r[j].action_index);
	      int rv2 = vnet_session_rule_add_del (&args);
	      vec_free (table_args->tag);
	      if (rv2)
		log_err ("rollback session_sdl add del returned on %U "
			 "@index %d: %U",
			 format_ip46_address, &table_args->lcl.fp_addr,
			 IP46_TYPE_ANY, j, format_session_error, rv2);
	    }
	  break;
	}
    }

done:
  REPLY_MACRO (VL_API_SESSION_SDL_ADD_DEL_REPLY);
}

static transport_proto_t
api_session_transport_proto_decode (const vl_api_transport_proto_t * api_tp)
{
  switch (*api_tp)
    {
    case TRANSPORT_PROTO_API_TCP:
      return TRANSPORT_PROTO_TCP;
    case TRANSPORT_PROTO_API_UDP:
      return TRANSPORT_PROTO_UDP;
    case TRANSPORT_PROTO_API_TLS:
      return TRANSPORT_PROTO_TLS;
    case TRANSPORT_PROTO_API_QUIC:
      return TRANSPORT_PROTO_QUIC;
    default:
      return TRANSPORT_PROTO_NONE;
    }
}

static vl_api_transport_proto_t
api_session_transport_proto_encode (const transport_proto_t tp)
{
  switch (tp)
    {
    case TRANSPORT_PROTO_TCP:
      return TRANSPORT_PROTO_API_TCP;
    case TRANSPORT_PROTO_UDP:
      return TRANSPORT_PROTO_API_UDP;
    case TRANSPORT_PROTO_TLS:
      return TRANSPORT_PROTO_API_TLS;
    case TRANSPORT_PROTO_QUIC:
      return TRANSPORT_PROTO_API_QUIC;
    default:
      return TRANSPORT_PROTO_API_NONE;
    }
}

static int
session_send_fds (vl_api_registration_t * reg, int fds[], int n_fds)
{
  clib_error_t *error;
  if (vl_api_registration_file_index (reg) == VL_API_INVALID_FI)
    return SESSION_E_BAPI_NO_FD;
  error = vl_api_send_fd_msg (reg, fds, n_fds);
  if (error)
    {
      clib_error_report (error);
      return SESSION_E_BAPI_SEND_FD;
    }
  return 0;
}

static int
mq_send_session_accepted_cb (session_t * s)
{
  app_worker_t *app_wrk = app_worker_get (s->app_wrk_index);
  session_accepted_msg_t m = { 0 };
  fifo_segment_t *eq_seg;
  session_t *listener;
  application_t *app;

  app = application_get (app_wrk->app_index);

  m.context = app->app_index;
  m.server_rx_fifo = fifo_segment_fifo_offset (s->rx_fifo);
  m.server_tx_fifo = fifo_segment_fifo_offset (s->tx_fifo);
  m.segment_handle = session_segment_handle (s);
  m.flags = s->flags;

  eq_seg = application_get_rx_mqs_segment (app);

  if (session_has_transport (s))
    {
      listener = listen_session_get_from_handle (s->listener_handle);
      m.listener_handle = app_listen_session_handle (listener);
      if (application_is_proxy (app))
	{
	  listener =
	    app_worker_first_listener (app_wrk, session_get_fib_proto (s),
				       session_get_transport_proto (s));
	  if (listener)
	    m.listener_handle = listen_session_get_handle (listener);
	}
      m.vpp_event_queue_address =
	fifo_segment_msg_q_offset (eq_seg, s->thread_index);
      m.mq_index = s->thread_index;
      m.handle = session_handle (s);

      session_get_endpoint (s, &m.rmt, 0 /* is_lcl */);
      session_get_endpoint (s, &m.lcl, 1 /* is_lcl */);
    }
  else
    {
      ct_connection_t *ct;

      ct = (ct_connection_t *) session_get_transport (s);
      listener = listen_session_get_from_handle (s->listener_handle);
      m.listener_handle = app_listen_session_handle (listener);
      m.rmt.is_ip4 = session_type_is_ip4 (listener->session_type);
      m.rmt.port = ct->c_rmt_port;
      m.lcl.port = ct->c_lcl_port;
      m.handle = session_handle (s);
      m.vpp_event_queue_address =
	fifo_segment_msg_q_offset (eq_seg, s->thread_index);
      m.mq_index = s->thread_index;
    }

  if (application_original_dst_is_enabled (app))
    {
      session_get_original_dst (&m.lcl, &m.rmt,
				session_get_transport_proto (s),
				&m.original_dst_ip4, &m.original_dst_port);
    }

  app_wrk_send_ctrl_evt (app_wrk, SESSION_CTRL_EVT_ACCEPTED, &m, sizeof (m));

  return 0;
}

static inline void
mq_send_session_close_evt (app_worker_t * app_wrk, session_handle_t sh,
			   session_evt_type_t evt_type)
{
  session_disconnected_msg_t m = { 0 };

  m.handle = sh;
  m.context = app_wrk->api_client_index;

  app_wrk_send_ctrl_evt (app_wrk, evt_type, &m, sizeof (m));
}

static inline void
mq_notify_close_subscribers (u32 app_index, session_handle_t sh,
			     svm_fifo_t * f, session_evt_type_t evt_type)
{
  app_worker_t *app_wrk;
  application_t *app;
  int i;

  app = application_get (app_index);
  if (!app)
    return;

  for (i = 0; i < f->shr->n_subscribers; i++)
    {
      if (!(app_wrk = application_get_worker (app, f->shr->subscribers[i])))
	continue;
      mq_send_session_close_evt (app_wrk, sh, SESSION_CTRL_EVT_DISCONNECTED);
    }
}

static void
mq_send_session_disconnected_cb (session_t * s)
{
  app_worker_t *app_wrk = app_worker_get (s->app_wrk_index);
  session_handle_t sh = session_handle (s);

  mq_send_session_close_evt (app_wrk, session_handle (s),
			     SESSION_CTRL_EVT_DISCONNECTED);

  if (svm_fifo_n_subscribers (s->rx_fifo))
    mq_notify_close_subscribers (app_wrk->app_index, sh, s->rx_fifo,
				 SESSION_CTRL_EVT_DISCONNECTED);
}

static void
mq_send_session_reset_cb (session_t * s)
{
  app_worker_t *app_wrk = app_worker_get (s->app_wrk_index);
  session_handle_t sh = session_handle (s);

  mq_send_session_close_evt (app_wrk, sh, SESSION_CTRL_EVT_RESET);

  if (svm_fifo_n_subscribers (s->rx_fifo))
    mq_notify_close_subscribers (app_wrk->app_index, sh, s->rx_fifo,
				 SESSION_CTRL_EVT_RESET);
}

int
mq_send_session_connected_cb (u32 app_wrk_index, u32 api_context,
			      session_t * s, session_error_t err)
{
  session_connected_msg_t m = { 0 };
  fifo_segment_t *eq_seg;
  app_worker_t *app_wrk;
  application_t *app;

  app_wrk = app_worker_get (app_wrk_index);

  m.context = api_context;
  m.retval = err;

  if (err)
    goto snd_msg;

  app = application_get (app_wrk->app_index);
  eq_seg = application_get_rx_mqs_segment (app);

  if (session_has_transport (s))
    {
      m.handle = session_handle (s);
      m.vpp_event_queue_address =
	fifo_segment_msg_q_offset (eq_seg, s->thread_index);

      session_get_endpoint (s, &m.lcl, 1 /* is_lcl */);

      m.server_rx_fifo = fifo_segment_fifo_offset (s->rx_fifo);
      m.server_tx_fifo = fifo_segment_fifo_offset (s->tx_fifo);
      m.segment_handle = session_segment_handle (s);
      m.mq_index = s->thread_index;
    }
  else
    {
      ct_connection_t *cct;

      cct = (ct_connection_t *) session_get_transport (s);
      m.handle = session_handle (s);
      m.lcl.port = cct->c_lcl_port;
      m.lcl.is_ip4 = cct->c_is_ip4;
      m.vpp_event_queue_address =
	fifo_segment_msg_q_offset (eq_seg, s->thread_index);
      m.server_rx_fifo = fifo_segment_fifo_offset (s->rx_fifo);
      m.server_tx_fifo = fifo_segment_fifo_offset (s->tx_fifo);
      m.segment_handle = session_segment_handle (s);
      m.mq_index = s->thread_index;
      m.ct_rx_fifo = fifo_segment_fifo_offset (cct->client_rx_fifo);
      m.ct_tx_fifo = fifo_segment_fifo_offset (cct->client_tx_fifo);
      m.ct_segment_handle = cct->segment_handle;
    }

  /* Setup client session index in advance, in case data arrives
   * before the app processes message and updates it */
  s->rx_fifo->shr->client_session_index = api_context;
  s->tx_fifo->shr->client_session_index = api_context;

snd_msg:

  app_wrk_send_ctrl_evt (app_wrk, SESSION_CTRL_EVT_CONNECTED, &m, sizeof (m));

  return 0;
}

static int
mq_send_session_bound_cb (u32 app_wrk_index, u32 api_context,
			  session_handle_t handle, int rv)
{
  session_bound_msg_t m = { 0 };
  transport_connection_t *ltc;
  fifo_segment_t *eq_seg;
  app_worker_t *app_wrk;
  application_t *app;
  app_listener_t *al;
  session_t *ls = 0;

  app_wrk = app_worker_get (app_wrk_index);

  m.context = api_context;
  m.retval = rv;

  if (rv)
    goto snd_msg;

  m.handle = handle;
  al = app_listener_get_w_handle (handle);
  if (al->session_index != SESSION_INVALID_INDEX)
    ls = app_listener_get_session (al);
  else
    ls = app_listener_get_local_session (al);

  ltc = session_get_transport (ls);
  m.lcl_port = ltc->lcl_port;
  m.lcl_is_ip4 = ltc->is_ip4;
  clib_memcpy_fast (m.lcl_ip, &ltc->lcl_ip, sizeof (m.lcl_ip));
  app = application_get (app_wrk->app_index);
  eq_seg = application_get_rx_mqs_segment (app);
  m.vpp_evt_q = fifo_segment_msg_q_offset (eq_seg, ls->thread_index);
  m.mq_index = ls->thread_index;

  if (transport_connection_is_cless (ltc))
    {
      session_t *wrk_ls;
      m.mq_index = transport_cl_thread ();
      m.vpp_evt_q = fifo_segment_msg_q_offset (eq_seg, m.mq_index);
      wrk_ls = app_listener_get_wrk_cl_session (al, app_wrk->wrk_map_index);
      m.rx_fifo = fifo_segment_fifo_offset (wrk_ls->rx_fifo);
      m.tx_fifo = fifo_segment_fifo_offset (wrk_ls->tx_fifo);
      m.segment_handle = session_segment_handle (wrk_ls);
    }

snd_msg:

  app_wrk_send_ctrl_evt (app_wrk, SESSION_CTRL_EVT_BOUND, &m, sizeof (m));

  return 0;
}

static void
mq_send_unlisten_cb (u32 app_wrk_index, session_handle_t sh, u32 context,
		     int rv)
{
  session_unlisten_reply_msg_t m = { 0 };
  app_worker_t *app_wrk;

  app_wrk = app_worker_get (app_wrk_index);

  m.context = context;
  m.handle = sh;
  m.retval = rv;
  app_wrk_send_ctrl_evt (app_wrk, SESSION_CTRL_EVT_UNLISTEN_REPLY, &m,
			 sizeof (m));
}

static void
mq_send_session_migrate_cb (session_t * s, session_handle_t new_sh)
{
  session_migrated_msg_t m = { 0 };
  fifo_segment_t *eq_seg;
  app_worker_t *app_wrk;
  application_t *app;
  u32 thread_index;

  thread_index = session_thread_from_handle (new_sh);
  app_wrk = app_worker_get (s->app_wrk_index);
  app = application_get (app_wrk->app_index);
  eq_seg = application_get_rx_mqs_segment (app);

  m.handle = session_handle (s);
  m.new_handle = new_sh;
  m.vpp_thread_index = thread_index;
  m.vpp_evt_q = fifo_segment_msg_q_offset (eq_seg, thread_index);
  m.segment_handle = SESSION_INVALID_HANDLE;

  app_wrk_send_ctrl_evt (app_wrk, SESSION_CTRL_EVT_MIGRATED, &m, sizeof (m));
}

static int
mq_send_add_segment_cb (u32 app_wrk_index, u64 segment_handle)
{
  session_app_add_segment_msg_t m = { 0 };
  vl_api_registration_t *reg;
  app_worker_t *app_wrk;
  fifo_segment_t *fs;
  ssvm_private_t *sp;
  u8 fd_flags = 0;

  app_wrk = app_worker_get (app_wrk_index);

  reg = vl_mem_api_client_index_to_registration (app_wrk->api_client_index);
  if (!reg)
    {
      clib_warning ("no api registration for client: %u",
		    app_wrk->api_client_index);
      return -1;
    }

  fs = segment_manager_get_segment_w_handle (segment_handle);
  sp = &fs->ssvm;
  if (ssvm_type (sp) == SSVM_SEGMENT_MEMFD)
    {
      if (vl_api_registration_file_index (reg) == VL_API_INVALID_FI)
	{
	  clib_warning ("can't send memfd fd");
	  return -1;
	}

      fd_flags |= SESSION_FD_F_MEMFD_SEGMENT;
    }

  m.segment_size = sp->ssvm_size;
  m.fd_flags = fd_flags;
  m.segment_handle = segment_handle;
  strncpy ((char *) m.segment_name, (char *) sp->name,
	   sizeof (m.segment_name) - 1);

  app_wrk_send_ctrl_evt_fd (app_wrk, SESSION_CTRL_EVT_APP_ADD_SEGMENT, &m,
			    sizeof (m), sp->fd);

  return 0;
}

static int
mq_send_del_segment_cb (u32 app_wrk_index, u64 segment_handle)
{
  session_app_del_segment_msg_t m = { 0 };
  vl_api_registration_t *reg;
  app_worker_t *app_wrk;

  app_wrk = app_worker_get (app_wrk_index);
  reg = vl_mem_api_client_index_to_registration (app_wrk->api_client_index);
  if (!reg)
    {
      clib_warning ("no registration: %u", app_wrk->api_client_index);
      return -1;
    }

  m.segment_handle = segment_handle;

  app_wrk_send_ctrl_evt (app_wrk, SESSION_CTRL_EVT_APP_DEL_SEGMENT, &m,
			 sizeof (m));

  return 0;
}

static void
mq_send_session_cleanup_cb (session_t * s, session_cleanup_ntf_t ntf)
{
  session_cleanup_msg_t m = { 0 };
  app_worker_t *app_wrk;

  /* Propagate transport cleanup notifications only if app didn't close */
  if (ntf == SESSION_CLEANUP_TRANSPORT
      && s->session_state != SESSION_STATE_TRANSPORT_DELETED)
    return;

  app_wrk = app_worker_get_if_valid (s->app_wrk_index);
  if (!app_wrk)
    return;

  m.handle = session_handle (s);
  m.type = ntf;

  app_wrk_send_ctrl_evt (app_wrk, SESSION_CTRL_EVT_CLEANUP, &m, sizeof (m));
}

static int
mq_send_io_rx_event (session_t *s)
{
  session_event_t *mq_evt;
  svm_msg_q_msg_t mq_msg;
  app_worker_t *app_wrk;
  svm_msg_q_t *mq;

  if (svm_fifo_has_event (s->rx_fifo))
    return 0;

  app_wrk = app_worker_get (s->app_wrk_index);
  mq = app_wrk->event_queue;

  mq_msg = svm_msg_q_alloc_msg_w_ring (mq, SESSION_MQ_IO_EVT_RING);
  mq_evt = svm_msg_q_msg_data (mq, &mq_msg);

  mq_evt->event_type = SESSION_IO_EVT_RX;
  mq_evt->session_index = s->rx_fifo->shr->client_session_index;

  (void) svm_fifo_set_event (s->rx_fifo);

  svm_msg_q_add_raw (mq, &mq_msg);

  return 0;
}

static int
mq_send_io_tx_event (session_t *s)
{
  app_worker_t *app_wrk = app_worker_get (s->app_wrk_index);
  svm_msg_q_t *mq = app_wrk->event_queue;
  session_event_t *mq_evt;
  svm_msg_q_msg_t mq_msg;

  mq_msg = svm_msg_q_alloc_msg_w_ring (mq, SESSION_MQ_IO_EVT_RING);
  mq_evt = svm_msg_q_msg_data (mq, &mq_msg);

  mq_evt->event_type = SESSION_IO_EVT_TX;
  mq_evt->session_index = s->tx_fifo->shr->client_session_index;

  svm_msg_q_add_raw (mq, &mq_msg);

  return 0;
}

static session_cb_vft_t session_mq_cb_vft = {
  .session_accept_callback = mq_send_session_accepted_cb,
  .session_disconnect_callback = mq_send_session_disconnected_cb,
  .session_connected_callback = mq_send_session_connected_cb,
  .session_reset_callback = mq_send_session_reset_cb,
  .session_migrate_callback = mq_send_session_migrate_cb,
  .session_cleanup_callback = mq_send_session_cleanup_cb,
  .session_listened_callback = mq_send_session_bound_cb,
  .session_unlistened_callback = mq_send_unlisten_cb,
  .add_segment_callback = mq_send_add_segment_cb,
  .del_segment_callback = mq_send_del_segment_cb,
  .builtin_app_rx_callback = mq_send_io_rx_event,
  .builtin_app_tx_callback = mq_send_io_tx_event,
};

static void
vl_api_session_enable_disable_t_handler (vl_api_session_enable_disable_t * mp)
{
  vl_api_session_enable_disable_reply_t *rmp;
  vlib_main_t *vm = vlib_get_main ();
  int rv = 0;
  session_enable_disable_args_t args;

  args.is_en = mp->is_enable;
  if (mp->is_enable)
    args.rt_engine_type = RT_BACKEND_ENGINE_RULE_TABLE;
  else
    args.rt_engine_type = RT_BACKEND_ENGINE_DISABLE;

  if (vnet_session_enable_disable (vm, &args))
    rv = VNET_API_ERROR_INVALID_ARGUMENT;
  REPLY_MACRO (VL_API_SESSION_ENABLE_DISABLE_REPLY);
}

static void
vl_api_session_enable_disable_v2_t_handler (
  vl_api_session_enable_disable_v2_t *mp)
{
  vl_api_session_enable_disable_v2_reply_t *rmp;
  vlib_main_t *vm = vlib_get_main ();
  int rv = 0;
  session_enable_disable_args_t args;

  STATIC_ASSERT ((session_rt_engine_type_t) RT_BACKEND_ENGINE_API_DISABLE ==
		   RT_BACKEND_ENGINE_DISABLE,
		 "API value mismatch");
  STATIC_ASSERT ((session_rt_engine_type_t) RT_BACKEND_ENGINE_API_NONE ==
		   RT_BACKEND_ENGINE_NONE,
		 "API value mismatch");
  STATIC_ASSERT ((session_rt_engine_type_t) RT_BACKEND_ENGINE_API_RULE_TABLE ==
		   RT_BACKEND_ENGINE_RULE_TABLE,
		 "API value mismatch");
  STATIC_ASSERT ((session_rt_engine_type_t) RT_BACKEND_ENGINE_API_SDL ==
		   RT_BACKEND_ENGINE_SDL,
		 "API value mismatch");

  args.rt_engine_type = (session_rt_engine_type_t) mp->rt_engine_type;
  if (args.rt_engine_type == RT_BACKEND_ENGINE_DISABLE)
    args.is_en = 0;
  else
    args.is_en = 1;

  if (vnet_session_enable_disable (vm, &args))
    rv = VNET_API_ERROR_INVALID_VALUE;

  REPLY_MACRO (VL_API_SESSION_ENABLE_DISABLE_V2_REPLY);
}

static void
vl_api_session_sapi_enable_disable_t_handler (
  vl_api_session_sapi_enable_disable_t *mp)
{
  vl_api_session_sapi_enable_disable_reply_t *rmp;
  int rv = 0;

  rv = appns_sapi_enable_disable (mp->is_enable);
  REPLY_MACRO (VL_API_SESSION_SAPI_ENABLE_DISABLE_REPLY);
}

static void
vl_api_app_attach_t_handler (vl_api_app_attach_t * mp)
{
  int rv = 0, *fds = 0, n_fds = 0, n_workers, i;
  fifo_segment_t *segp, *rx_mqs_seg = 0;
  vnet_app_attach_args_t _a, *a = &_a;
  vl_api_app_attach_reply_t *rmp;
  u8 fd_flags = 0, ctrl_thread;
  vl_api_registration_t *reg;
  svm_msg_q_t *rx_mq;
  application_t *app;

  reg = vl_api_client_index_to_registration (mp->client_index);
  if (!reg)
    return;

  n_workers = vlib_num_workers ();
  if (!session_main_is_enabled () || appns_sapi_enabled ())
    {
      rv = VNET_API_ERROR_FEATURE_DISABLED;
      goto done;
    }
  /* Only support binary api with socket transport */
  if (vl_api_registration_file_index (reg) == VL_API_INVALID_FI)
    {
      rv = VNET_API_ERROR_APP_UNSUPPORTED_CFG;
      goto done;
    }

  STATIC_ASSERT (sizeof (u64) * APP_OPTIONS_N_OPTIONS <=
		 sizeof (mp->options),
		 "Out of options, fix api message definition");

  clib_memset (a, 0, sizeof (*a));
  a->api_client_index = mp->client_index;
  a->options = mp->options;
  a->session_cb_vft = &session_mq_cb_vft;
  a->namespace_id = vl_api_from_api_to_new_vec (mp, &mp->namespace_id);

  if ((rv = vnet_application_attach (a)))
    {
      clib_warning ("attach returned: %U", format_session_error, rv);
      rv = VNET_API_ERROR_UNSPECIFIED;
      vec_free (a->namespace_id);
      goto done;
    }
  vec_free (a->namespace_id);

  vec_validate (fds, 3 /* segs + tx evtfd */ + n_workers);

  /* Send rx mqs segment */
  app = application_get (a->app_index);
  rx_mqs_seg = application_get_rx_mqs_segment (app);

  fd_flags |= SESSION_FD_F_VPP_MQ_SEGMENT;
  fds[n_fds] = rx_mqs_seg->ssvm.fd;
  n_fds += 1;

  /* Send fifo segment fd if needed */
  if (ssvm_type (a->segment) == SSVM_SEGMENT_MEMFD)
    {
      fd_flags |= SESSION_FD_F_MEMFD_SEGMENT;
      fds[n_fds] = a->segment->fd;
      n_fds += 1;
    }
  if (a->options[APP_OPTIONS_FLAGS] & APP_OPTIONS_FLAGS_EVT_MQ_USE_EVENTFD)
    {
      fd_flags |= SESSION_FD_F_MQ_EVENTFD;
      fds[n_fds] = svm_msg_q_get_eventfd (a->app_evt_q);
      n_fds += 1;
    }

  if (application_use_private_rx_mqs ())
    {
      fd_flags |= SESSION_FD_F_VPP_MQ_EVENTFD;
      for (i = 0; i < n_workers + 1; i++)
	{
	  rx_mq = application_rx_mq_get (app, i);
	  fds[n_fds] = svm_msg_q_get_eventfd (rx_mq);
	  n_fds += 1;
	}
    }

done:
  REPLY_MACRO3 (
    VL_API_APP_ATTACH_REPLY,
    ((!rv) ? vec_len (((fifo_segment_t *) a->segment)->ssvm.name) : 0), ({
      if (!rv)
	{
	  ctrl_thread = n_workers ? 1 : 0;
	  segp = (fifo_segment_t *) a->segment;
	  rmp->app_index = clib_host_to_net_u32 (a->app_index);
	  rmp->app_mq = fifo_segment_msg_q_offset (segp, 0);
	  rmp->vpp_ctrl_mq =
	    fifo_segment_msg_q_offset (rx_mqs_seg, ctrl_thread);
	  rmp->vpp_ctrl_mq_thread = ctrl_thread;
	  rmp->n_fds = n_fds;
	  rmp->fd_flags = fd_flags;
	  if (vec_len (segp->ssvm.name))
	    {
	      vl_api_vec_to_api_string (segp->ssvm.name, &rmp->segment_name);
	    }
	  rmp->segment_size = segp->ssvm.ssvm_size;
	  rmp->segment_handle = clib_host_to_net_u64 (a->segment_handle);
	}
    }));

  if (n_fds)
    session_send_fds (reg, fds, n_fds);
  vec_free (fds);
}

static void
vl_api_app_worker_add_del_t_handler (vl_api_app_worker_add_del_t * mp)
{
  int rv = 0, fds[SESSION_N_FD_TYPE], n_fds = 0;
  vl_api_app_worker_add_del_reply_t *rmp;
  vl_api_registration_t *reg;
  application_t *app;
  u8 fd_flags = 0;

  if (!session_main_is_enabled () || appns_sapi_enabled ())
    {
      rv = VNET_API_ERROR_FEATURE_DISABLED;
      goto done;
    }

  reg = vl_api_client_index_to_registration (mp->client_index);
  if (!reg)
    return;

  app = application_get_if_valid (clib_net_to_host_u32 (mp->app_index));
  if (!app)
    {
      rv = VNET_API_ERROR_INVALID_VALUE;
      goto done;
    }

  vnet_app_worker_add_del_args_t args = {
    .app_index = app->app_index,
    .wrk_map_index = clib_net_to_host_u32 (mp->wrk_index),
    .api_client_index = mp->client_index,
    .is_add = mp->is_add
  };
  rv = vnet_app_worker_add_del (&args);
  if (rv)
    {
      clib_warning ("app worker add/del returned: %U", format_session_error,
		    rv);
      rv = VNET_API_ERROR_UNSPECIFIED;
      goto done;
    }

  if (!mp->is_add)
    goto done;

  /* Send fifo segment fd if needed */
  if (ssvm_type (args.segment) == SSVM_SEGMENT_MEMFD)
    {
      fd_flags |= SESSION_FD_F_MEMFD_SEGMENT;
      fds[n_fds] = args.segment->fd;
      n_fds += 1;
    }
  if (application_segment_manager_properties (app)->use_mq_eventfd)
    {
      fd_flags |= SESSION_FD_F_MQ_EVENTFD;
      fds[n_fds] = svm_msg_q_get_eventfd (args.evt_q);
      n_fds += 1;
    }

done:
  REPLY_MACRO3 (
    VL_API_APP_WORKER_ADD_DEL_REPLY,
    ((!rv && mp->is_add) ? vec_len (args.segment->name) : 0), ({
      rmp->is_add = mp->is_add;
      rmp->wrk_index = mp->wrk_index;
      if (!rv && mp->is_add)
	{
	  rmp->wrk_index = clib_host_to_net_u32 (args.wrk_map_index);
	  rmp->segment_handle = clib_host_to_net_u64 (args.segment_handle);
	  rmp->app_event_queue_address =
	    fifo_segment_msg_q_offset ((fifo_segment_t *) args.segment, 0);
	  rmp->n_fds = n_fds;
	  rmp->fd_flags = fd_flags;
	  if (vec_len (args.segment->name))
	    {
	      vl_api_vec_to_api_string (args.segment->name,
					&rmp->segment_name);
	    }
	}
    }));

  if (n_fds)
    session_send_fds (reg, fds, n_fds);
}

static void
vl_api_application_detach_t_handler (vl_api_application_detach_t * mp)
{
  vl_api_application_detach_reply_t *rmp;
  int rv = VNET_API_ERROR_INVALID_VALUE_2;
  vnet_app_detach_args_t _a, *a = &_a;
  application_t *app;

  if (!session_main_is_enabled () || appns_sapi_enabled ())
    {
      rv = VNET_API_ERROR_FEATURE_DISABLED;
      goto done;
    }

  app = application_lookup (mp->client_index);
  if (app)
    {
      a->app_index = app->app_index;
      a->api_client_index = mp->client_index;
      rv = vnet_application_detach (a);
      if (rv)
	{
	  clib_warning ("vnet_application_detach: %U", format_session_error,
			rv);
	  rv = VNET_API_ERROR_UNSPECIFIED;
	}
    }

done:
  REPLY_MACRO (VL_API_APPLICATION_DETACH_REPLY);
}

static void
vl_api_app_namespace_add_del_t_handler (vl_api_app_namespace_add_del_t * mp)
{
  vl_api_app_namespace_add_del_reply_t *rmp;
  u32 appns_index = 0;
  u8 *ns_id = 0;
  int rv = 0;
  if (session_main_is_enabled () == 0)
    {
      rv = VNET_API_ERROR_FEATURE_DISABLED;
      goto done;
    }

  ns_id = vl_api_from_api_to_new_vec (mp, &mp->namespace_id);

  vnet_app_namespace_add_del_args_t args = {
    .ns_id = ns_id,
    .sock_name = 0,
    .secret = clib_net_to_host_u64 (mp->secret),
    .sw_if_index = clib_net_to_host_u32 (mp->sw_if_index),
    .ip4_fib_id = clib_net_to_host_u32 (mp->ip4_fib_id),
    .ip6_fib_id = clib_net_to_host_u32 (mp->ip6_fib_id),
    .is_add = 1
  };
  rv = vnet_app_namespace_add_del (&args);
  if (!rv)
    {
      appns_index = app_namespace_index_from_id (ns_id);
      if (appns_index == APP_NAMESPACE_INVALID_INDEX)
	{
	  clib_warning ("app ns lookup failed");
	  rv = VNET_API_ERROR_UNSPECIFIED;
	}
    }
  vec_free (ns_id);

done:
  REPLY_MACRO2 (VL_API_APP_NAMESPACE_ADD_DEL_REPLY, ({
    if (!rv)
      rmp->appns_index = clib_host_to_net_u32 (appns_index);
  }));
}

static void
vl_api_app_namespace_add_del_v2_t_handler (
  vl_api_app_namespace_add_del_v2_t *mp)
{
  vl_api_app_namespace_add_del_v2_reply_t *rmp;
  u8 *ns_id = 0;
  u32 appns_index = 0;
  int rv = 0;

  if (session_main_is_enabled () == 0)
    {
      rv = VNET_API_ERROR_FEATURE_DISABLED;
      goto done;
    }

  mp->namespace_id[sizeof (mp->namespace_id) - 1] = 0;
  ns_id = format (0, "%s", &mp->namespace_id);

  vnet_app_namespace_add_del_args_t args = {
    .ns_id = ns_id,
    .sock_name = 0,
    .secret = clib_net_to_host_u64 (mp->secret),
    .sw_if_index = clib_net_to_host_u32 (mp->sw_if_index),
    .ip4_fib_id = clib_net_to_host_u32 (mp->ip4_fib_id),
    .ip6_fib_id = clib_net_to_host_u32 (mp->ip6_fib_id),
    .is_add = 1
  };
  rv = vnet_app_namespace_add_del (&args);
  if (!rv)
    {
      appns_index = app_namespace_index_from_id (ns_id);
      if (appns_index == APP_NAMESPACE_INVALID_INDEX)
	{
	  clib_warning ("app ns lookup failed id:%s", ns_id);
	  rv = VNET_API_ERROR_UNSPECIFIED;
	}
    }
  vec_free (ns_id);

done:
  REPLY_MACRO2 (VL_API_APP_NAMESPACE_ADD_DEL_V2_REPLY, ({
		  if (!rv)
		    rmp->appns_index = clib_host_to_net_u32 (appns_index);
		}));
}

static void
vl_api_app_namespace_add_del_v4_t_handler (
  vl_api_app_namespace_add_del_v4_t *mp)
{
  vl_api_app_namespace_add_del_v4_reply_t *rmp;
  u8 *ns_id = 0, *sock_name = 0;
  u32 appns_index = 0;
  int rv = 0;
  if (session_main_is_enabled () == 0)
    {
      rv = VNET_API_ERROR_FEATURE_DISABLED;
      goto done;
    }
  mp->namespace_id[sizeof (mp->namespace_id) - 1] = 0;
  ns_id = format (0, "%s", &mp->namespace_id);
  sock_name = vl_api_from_api_to_new_vec (mp, &mp->sock_name);
  vnet_app_namespace_add_del_args_t args = {
    .ns_id = ns_id,
    .sock_name = sock_name,
    .secret = clib_net_to_host_u64 (mp->secret),
    .sw_if_index = clib_net_to_host_u32 (mp->sw_if_index),
    .ip4_fib_id = clib_net_to_host_u32 (mp->ip4_fib_id),
    .ip6_fib_id = clib_net_to_host_u32 (mp->ip6_fib_id),
    .is_add = mp->is_add,
  };
  rv = vnet_app_namespace_add_del (&args);
  if (!rv && mp->is_add)
    {
      appns_index = app_namespace_index_from_id (ns_id);
      if (appns_index == APP_NAMESPACE_INVALID_INDEX)
	{
	  clib_warning ("app ns lookup failed id:%s", ns_id);
	  rv = VNET_API_ERROR_UNSPECIFIED;
	}
    }
  vec_free (ns_id);
  vec_free (sock_name);
done:
  REPLY_MACRO2 (VL_API_APP_NAMESPACE_ADD_DEL_V4_REPLY, ({
		  if (!rv)
		    rmp->appns_index = clib_host_to_net_u32 (appns_index);
		}));
}

static void
vl_api_app_namespace_add_del_v3_t_handler (
  vl_api_app_namespace_add_del_v3_t *mp)
{
  vl_api_app_namespace_add_del_v3_reply_t *rmp;
  u8 *ns_id = 0, *sock_name = 0, *api_sock_name = 0;
  u32 appns_index = 0;
  int rv = 0;
  if (session_main_is_enabled () == 0)
    {
      rv = VNET_API_ERROR_FEATURE_DISABLED;
      goto done;
    }
  mp->namespace_id[sizeof (mp->namespace_id) - 1] = 0;
  ns_id = format (0, "%s", &mp->namespace_id);
  api_sock_name = vl_api_from_api_to_new_vec (mp, &mp->sock_name);
  mp->netns[sizeof (mp->netns) - 1] = 0;
  if (strlen ((char *) mp->netns) != 0)
    {
      sock_name =
	format (0, "abstract:%v,netns_name=%s", api_sock_name, &mp->netns);
    }
  else
    {
      sock_name = api_sock_name;
      api_sock_name = 0; // for vec_free
    }

  vnet_app_namespace_add_del_args_t args = {
    .ns_id = ns_id,
    .sock_name = sock_name,
    .secret = clib_net_to_host_u64 (mp->secret),
    .sw_if_index = clib_net_to_host_u32 (mp->sw_if_index),
    .ip4_fib_id = clib_net_to_host_u32 (mp->ip4_fib_id),
    .ip6_fib_id = clib_net_to_host_u32 (mp->ip6_fib_id),
    .is_add = mp->is_add,
  };
  rv = vnet_app_namespace_add_del (&args);
  if (!rv && mp->is_add)
    {
      appns_index = app_namespace_index_from_id (ns_id);
      if (appns_index == APP_NAMESPACE_INVALID_INDEX)
	{
	  clib_warning ("app ns lookup failed id:%s", ns_id);
	  rv = VNET_API_ERROR_UNSPECIFIED;
	}
    }
  vec_free (ns_id);
  vec_free (sock_name);
  vec_free (api_sock_name);
done:
  REPLY_MACRO2 (VL_API_APP_NAMESPACE_ADD_DEL_V3_REPLY, ({
		  if (!rv)
		    rmp->appns_index = clib_host_to_net_u32 (appns_index);
		}));
}

static void
vl_api_session_rule_add_del_t_handler (vl_api_session_rule_add_del_t * mp)
{
  vl_api_session_rule_add_del_reply_t *rmp;
  session_rule_add_del_args_t args;
  session_rule_table_add_del_args_t *table_args = &args.table_args;
  int rv = 0;

  if (session_main_is_enabled () == 0)
    {
      rv = VNET_API_ERROR_FEATURE_DISABLED;
      goto done;
    }

  clib_memset (&args, 0, sizeof (args));

  ip_prefix_decode (&mp->lcl, &table_args->lcl);
  ip_prefix_decode (&mp->rmt, &table_args->rmt);

  table_args->lcl_port = mp->lcl_port;
  table_args->rmt_port = mp->rmt_port;
  table_args->action_index = clib_net_to_host_u32 (mp->action_index);
  table_args->is_add = mp->is_add;
  mp->tag[sizeof (mp->tag) - 1] = 0;
  table_args->tag = format (0, "%s", mp->tag);
  args.appns_index = clib_net_to_host_u32 (mp->appns_index);
  args.scope = mp->scope;
  args.transport_proto =
    api_session_transport_proto_decode (&mp->transport_proto) ==
    TRANSPORT_PROTO_UDP ? 1 : 0;

  rv = vnet_session_rule_add_del (&args);
  if (rv)
    {
      clib_warning ("rule add del returned: %U", format_session_error, rv);
      rv = VNET_API_ERROR_UNSPECIFIED;
    }
  vec_free (table_args->tag);
done:
  REPLY_MACRO (VL_API_SESSION_RULE_ADD_DEL_REPLY);
}

static void
send_session_rule_details4 (mma_rule_16_t * rule, u8 is_local,
			    u8 transport_proto, u32 appns_index, u8 * tag,
			    vl_api_registration_t * reg, u32 context)
{
  vl_api_session_rules_details_t *rmp = 0;
  session_mask_or_match_4_t *match =
    (session_mask_or_match_4_t *) & rule->match;
  session_mask_or_match_4_t *mask =
    (session_mask_or_match_4_t *) & rule->mask;
  fib_prefix_t lcl, rmt;

  rmp = vl_msg_api_alloc (sizeof (*rmp));
  clib_memset (rmp, 0, sizeof (*rmp));
  rmp->_vl_msg_id = ntohs (REPLY_MSG_ID_BASE + VL_API_SESSION_RULES_DETAILS);
  rmp->context = context;

  clib_memset (&lcl, 0, sizeof (lcl));
  clib_memset (&rmt, 0, sizeof (rmt));
  ip_set (&lcl.fp_addr, &match->lcl_ip, 1);
  ip_set (&rmt.fp_addr, &match->rmt_ip, 1);
  lcl.fp_len = ip4_mask_to_preflen (&mask->lcl_ip);
  rmt.fp_len = ip4_mask_to_preflen (&mask->rmt_ip);
  lcl.fp_proto = FIB_PROTOCOL_IP4;
  rmt.fp_proto = FIB_PROTOCOL_IP4;

  ip_prefix_encode (&lcl, &rmp->lcl);
  ip_prefix_encode (&rmt, &rmp->rmt);
  rmp->lcl_port = match->lcl_port;
  rmp->rmt_port = match->rmt_port;
  rmp->action_index = clib_host_to_net_u32 (rule->action_index);
  rmp->scope =
    is_local ? SESSION_RULE_SCOPE_API_LOCAL : SESSION_RULE_SCOPE_API_GLOBAL;
  rmp->transport_proto = api_session_transport_proto_encode (transport_proto);
  rmp->appns_index = clib_host_to_net_u32 (appns_index);
  if (tag)
    {
      clib_memcpy_fast (rmp->tag, tag, vec_len (tag));
      rmp->tag[vec_len (tag)] = 0;
    }

  vl_api_send_msg (reg, (u8 *) rmp);
}

static void
send_session_rule_details6 (mma_rule_40_t * rule, u8 is_local,
			    u8 transport_proto, u32 appns_index, u8 * tag,
			    vl_api_registration_t * reg, u32 context)
{
  vl_api_session_rules_details_t *rmp = 0;
  session_mask_or_match_6_t *match =
    (session_mask_or_match_6_t *) & rule->match;
  session_mask_or_match_6_t *mask =
    (session_mask_or_match_6_t *) & rule->mask;
  fib_prefix_t lcl, rmt;

  rmp = vl_msg_api_alloc (sizeof (*rmp));
  clib_memset (rmp, 0, sizeof (*rmp));
  rmp->_vl_msg_id = ntohs (REPLY_MSG_ID_BASE + VL_API_SESSION_RULES_DETAILS);
  rmp->context = context;

  clib_memset (&lcl, 0, sizeof (lcl));
  clib_memset (&rmt, 0, sizeof (rmt));
  ip_set (&lcl.fp_addr, &match->lcl_ip, 0);
  ip_set (&rmt.fp_addr, &match->rmt_ip, 0);
  lcl.fp_len = ip6_mask_to_preflen (&mask->lcl_ip);
  rmt.fp_len = ip6_mask_to_preflen (&mask->rmt_ip);
  lcl.fp_proto = FIB_PROTOCOL_IP6;
  rmt.fp_proto = FIB_PROTOCOL_IP6;

  ip_prefix_encode (&lcl, &rmp->lcl);
  ip_prefix_encode (&rmt, &rmp->rmt);
  rmp->lcl_port = match->lcl_port;
  rmp->rmt_port = match->rmt_port;
  rmp->action_index = clib_host_to_net_u32 (rule->action_index);
  rmp->scope =
    is_local ? SESSION_RULE_SCOPE_API_LOCAL : SESSION_RULE_SCOPE_API_GLOBAL;
  rmp->transport_proto = api_session_transport_proto_encode (transport_proto);
  rmp->appns_index = clib_host_to_net_u32 (appns_index);
  if (tag)
    {
      clib_memcpy_fast (rmp->tag, tag, vec_len (tag));
      rmp->tag[vec_len (tag)] = 0;
    }

  vl_api_send_msg (reg, (u8 *) rmp);
}

static void
send_session_rules_table_details (session_rules_table_t * srt, u8 fib_proto,
				  u8 tp, u8 is_local, u32 appns_index,
				  vl_api_registration_t * reg, u32 context)
{
  mma_rule_16_t *rule16;
  mma_rule_40_t *rule40;
  mma_rules_table_16_t *srt16;
  mma_rules_table_40_t *srt40;
  u32 ri;

  if (is_local || fib_proto == FIB_PROTOCOL_IP4)
    {
      u8 *tag = 0;
      srt16 = &srt->session_rules_tables_16;
      pool_foreach (rule16, srt16->rules)  {
	ri = mma_rules_table_rule_index_16 (srt16, rule16);
	tag = session_rules_table_rule_tag (srt, ri, 1);
        send_session_rule_details4 (rule16, is_local, tp, appns_index, tag,
                                    reg, context);
      }
    }
  if (is_local || fib_proto == FIB_PROTOCOL_IP6)
    {
      u8 *tag = 0;
      srt40 = &srt->session_rules_tables_40;
      pool_foreach (rule40, srt40->rules)  {
	ri = mma_rules_table_rule_index_40 (srt40, rule40);
	tag = session_rules_table_rule_tag (srt, ri, 1);
        send_session_rule_details6 (rule40, is_local, tp, appns_index, tag,
                                    reg, context);
      }
    }
}

static void
vl_api_session_rules_dump_t_handler (vl_api_session_rules_dump_t * mp)
{
  vl_api_registration_t *reg;
  session_table_t *st;
  u8 tp;

  reg = vl_api_client_index_to_registration (mp->client_index);
  if (!reg)
    return;

  session_table_foreach (st, ({
			   if (st->srtg_handle != SESSION_SRTG_HANDLE_INVALID)
			     for (tp = 0; tp < TRANSPORT_N_PROTOS; tp++)
			       {
				 session_rules_table_t *srt =
				   srtg_handle_to_srt (st->srtg_handle, tp);
				 send_session_rules_table_details (
				   srt, st->active_fib_proto, tp, st->is_local,
				   st->appns_index, reg, mp->context);
			       }
			 }));
}

typedef struct session_sdl_table_walk_ctx_
{
  vl_api_registration_t *reg;
  u32 mp_context;
  u32 appns_index;
} session_sdl_table_walk_ctx;

static void
send_session_sdl_details (u32 fei, ip46_address_t *lcl_ip, u16 fp_len,
			  u32 action_index, u32 fp_proto, u8 *tag, void *args)
{
  session_sdl_table_walk_ctx *ctx = args;
  vl_api_registration_t *reg = ctx->reg;
  u32 appns_index = ctx->appns_index;
  u32 context = ctx->mp_context;
  vl_api_session_sdl_details_t *rmp = 0;
  fib_prefix_t lcl;

  rmp = vl_msg_api_alloc (sizeof (*rmp));
  clib_memset (rmp, 0, sizeof (*rmp));
  rmp->_vl_msg_id = ntohs (REPLY_MSG_ID_BASE + VL_API_SESSION_SDL_DETAILS);
  rmp->context = context;

  clib_memset (&lcl, 0, sizeof (lcl));
  if (fp_proto == FIB_PROTOCOL_IP4)
    ip_set (&lcl.fp_addr, &lcl_ip->ip4, 1);
  else
    ip_set (&lcl.fp_addr, &lcl_ip->ip6, 0);
  lcl.fp_len = fp_len;
  lcl.fp_proto = fp_proto,

  ip_prefix_encode (&lcl, &rmp->lcl);
  rmp->action_index = clib_host_to_net_u32 (action_index);
  rmp->appns_index = clib_host_to_net_u32 (appns_index);
  if (tag)
    {
      clib_memcpy_fast (rmp->tag, tag, vec_len (tag));
      rmp->tag[vec_len (tag)] = 0;
    }

  vl_api_send_msg (reg, (u8 *) rmp);
}

static void
vl_api_session_sdl_dump_t_handler (vl_api_session_rules_dump_t *mp)
{
  vl_api_registration_t *reg;
  session_table_t *st;
  session_sdl_table_walk_ctx ctx;

  reg = vl_api_client_index_to_registration (mp->client_index);
  if (!reg)
    return;

  ctx.reg = reg;
  ctx.mp_context = mp->context;

  session_table_foreach (
    st, ({
      if (st->srtg_handle != SESSION_SRTG_HANDLE_INVALID)
	{
	  ctx.appns_index = st->appns_index;

	  if (st->active_fib_proto == FIB_PROTOCOL_IP4)
	    session_sdl_table_walk4 (st->srtg_handle, send_session_sdl_details,
				     &ctx);
	  else
	    session_sdl_table_walk6 (st->srtg_handle, send_session_sdl_details,
				     &ctx);
	}
    }));
}

static void
vl_api_app_add_cert_key_pair_t_handler (vl_api_app_add_cert_key_pair_t * mp)
{
  vl_api_app_add_cert_key_pair_reply_t *rmp;
  vnet_app_add_cert_key_pair_args_t _a, *a = &_a;
  u32 certkey_len, key_len, cert_len;
  int rv = 0;
  if (session_main_is_enabled () == 0)
    {
      rv = VNET_API_ERROR_FEATURE_DISABLED;
      goto done;
    }

  cert_len = clib_net_to_host_u16 (mp->cert_len);
  if (cert_len > 10000)
    {
      rv = VNET_API_ERROR_INVALID_VALUE;
      goto done;
    }

  certkey_len = clib_net_to_host_u16 (mp->certkey_len);
  if (certkey_len < cert_len)
    {
      rv = VNET_API_ERROR_INVALID_VALUE;
      goto done;
    }

  key_len = certkey_len - cert_len;
  if (key_len > 10000)
    {
      rv = VNET_API_ERROR_INVALID_VALUE;
      goto done;
    }

  clib_memset (a, 0, sizeof (*a));
  a->cert = mp->certkey;
  a->key = mp->certkey + cert_len;
  a->cert_len = cert_len;
  a->key_len = key_len;
  rv = vnet_app_add_cert_key_pair (a);

done:
  REPLY_MACRO2 (VL_API_APP_ADD_CERT_KEY_PAIR_REPLY, ({
    if (!rv)
      rmp->index = clib_host_to_net_u32 (a->index);
  }));
}

static void
vl_api_app_del_cert_key_pair_t_handler (vl_api_app_del_cert_key_pair_t * mp)
{
  vl_api_app_del_cert_key_pair_reply_t *rmp;
  u32 ckpair_index;
  int rv = 0;
  if (session_main_is_enabled () == 0)
    {
      rv = VNET_API_ERROR_FEATURE_DISABLED;
      goto done;
    }
  ckpair_index = clib_net_to_host_u32 (mp->index);
  rv = vnet_app_del_cert_key_pair (ckpair_index);
  if (rv)
    {
      clib_warning ("vnet_app_del_cert_key_pair: %U", format_session_error,
		    rv);
      rv = VNET_API_ERROR_UNSPECIFIED;
    }

done:
  REPLY_MACRO (VL_API_APP_DEL_CERT_KEY_PAIR_REPLY);
}

static clib_error_t *
application_reaper_cb (u32 client_index)
{
  application_t *app = application_lookup (client_index);
  vnet_app_detach_args_t _a, *a = &_a;
  if (app)
    {
      a->app_index = app->app_index;
      a->api_client_index = client_index;
      vnet_application_detach (a);
    }
  return 0;
}

VL_MSG_API_REAPER_FUNCTION (application_reaper_cb);

/*
 * Socket api functions
 */

static int
mq_send_add_segment_sapi_cb (u32 app_wrk_index, u64 segment_handle)
{
  session_app_add_segment_msg_t m = { 0 };
  app_worker_t *app_wrk;
  fifo_segment_t *fs;
  ssvm_private_t *sp;
  u8 fd_flags = 0;

  app_wrk = app_worker_get (app_wrk_index);

  fs = segment_manager_get_segment_w_handle (segment_handle);
  sp = &fs->ssvm;
  ASSERT (ssvm_type (sp) == SSVM_SEGMENT_MEMFD);

  fd_flags |= SESSION_FD_F_MEMFD_SEGMENT;

  m.segment_size = sp->ssvm_size;
  m.fd_flags = fd_flags;
  m.segment_handle = segment_handle;
  strncpy ((char *) m.segment_name, (char *) sp->name,
	   sizeof (m.segment_name) - 1);

  app_wrk_send_ctrl_evt_fd (app_wrk, SESSION_CTRL_EVT_APP_ADD_SEGMENT, &m,
			    sizeof (m), sp->fd);

  return 0;
}

static int
mq_send_del_segment_sapi_cb (u32 app_wrk_index, u64 segment_handle)
{
  session_app_del_segment_msg_t m = { 0 };
  app_worker_t *app_wrk;

  app_wrk = app_worker_get (app_wrk_index);

  m.segment_handle = segment_handle;

  app_wrk_send_ctrl_evt (app_wrk, SESSION_CTRL_EVT_APP_DEL_SEGMENT, &m,
			 sizeof (m));

  return 0;
}

static session_cb_vft_t session_mq_sapi_cb_vft = {
  .session_accept_callback = mq_send_session_accepted_cb,
  .session_disconnect_callback = mq_send_session_disconnected_cb,
  .session_connected_callback = mq_send_session_connected_cb,
  .session_reset_callback = mq_send_session_reset_cb,
  .session_migrate_callback = mq_send_session_migrate_cb,
  .session_cleanup_callback = mq_send_session_cleanup_cb,
  .session_listened_callback = mq_send_session_bound_cb,
  .session_unlistened_callback = mq_send_unlisten_cb,
  .add_segment_callback = mq_send_add_segment_sapi_cb,
  .del_segment_callback = mq_send_del_segment_sapi_cb,
  .builtin_app_rx_callback = mq_send_io_rx_event,
  .builtin_app_tx_callback = mq_send_io_tx_event,
};

static void
session_api_attach_handler (app_namespace_t * app_ns, clib_socket_t * cs,
			    app_sapi_attach_msg_t * mp)
{
  int rv = 0, *fds = 0, n_fds = 0, i, n_workers;
  vnet_app_attach_args_t _a, *a = &_a;
  app_sapi_attach_reply_msg_t *rmp;
  u8 fd_flags = 0, ctrl_thread;
  app_ns_api_handle_t *handle;
  fifo_segment_t *rx_mqs_seg;
  app_sapi_msg_t msg = { 0 };
  app_worker_t *app_wrk;
  application_t *app;
  svm_msg_q_t *rx_mq;

  /* Make sure name is null terminated */
  mp->name[63] = 0;

  clib_memset (a, 0, sizeof (*a));
  a->api_client_index = appns_sapi_socket_handle (app_ns, cs);
  a->name = format (0, "%s", (char *) mp->name);
  a->options = mp->options;
  a->session_cb_vft = &session_mq_sapi_cb_vft;
  a->use_sock_api = 1;
  a->options[APP_OPTIONS_NAMESPACE] = app_namespace_index (app_ns);

  if ((rv = vnet_application_attach (a)))
    {
      clib_warning ("attach returned: %d", rv);
      goto done;
    }

  n_workers = vlib_num_workers ();
  vec_validate (fds, 3 /* segs + tx evtfd */ + n_workers);

  /* Send event queues segment */
  app = application_get (a->app_index);
  rx_mqs_seg = application_get_rx_mqs_segment (app);

  fd_flags |= SESSION_FD_F_VPP_MQ_SEGMENT;
  fds[n_fds] = rx_mqs_seg->ssvm.fd;
  n_fds += 1;

  /* Send fifo segment fd if needed */
  if (ssvm_type (a->segment) == SSVM_SEGMENT_MEMFD)
    {
      fd_flags |= SESSION_FD_F_MEMFD_SEGMENT;
      fds[n_fds] = a->segment->fd;
      n_fds += 1;
    }
  if (a->options[APP_OPTIONS_FLAGS] & APP_OPTIONS_FLAGS_EVT_MQ_USE_EVENTFD)
    {
      fd_flags |= SESSION_FD_F_MQ_EVENTFD;
      fds[n_fds] = svm_msg_q_get_eventfd (a->app_evt_q);
      n_fds += 1;
    }

  if (application_use_private_rx_mqs ())
    {
      fd_flags |= SESSION_FD_F_VPP_MQ_EVENTFD;
      for (i = 0; i < n_workers + 1; i++)
	{
	  rx_mq = application_rx_mq_get (app, i);
	  fds[n_fds] = svm_msg_q_get_eventfd (rx_mq);
	  n_fds += 1;
	}
    }

done:

  msg.type = APP_SAPI_MSG_TYPE_ATTACH_REPLY;
  rmp = &msg.attach_reply;
  rmp->retval = rv;
  if (!rv)
    {
      ctrl_thread = n_workers ? 1 : 0;
      rmp->app_index = a->app_index;
      rmp->app_mq =
	fifo_segment_msg_q_offset ((fifo_segment_t *) a->segment, 0);
      rmp->vpp_ctrl_mq = fifo_segment_msg_q_offset (rx_mqs_seg, ctrl_thread);
      rmp->vpp_ctrl_mq_thread = ctrl_thread;
      rmp->n_fds = n_fds;
      rmp->fd_flags = fd_flags;
      /* No segment name and size since we only support memfds
       * in this configuration */
      rmp->segment_handle = a->segment_handle;
      rmp->api_client_handle = a->api_client_index;

      /* Update app index for socket */
      handle = (app_ns_api_handle_t *) & cs->private_data;
      app_wrk = application_get_worker (app, 0);
      handle->aah_app_wrk_index = app_wrk->wrk_index;
    }

  clib_socket_sendmsg (cs, &msg, sizeof (msg), fds, n_fds);
  vec_free (a->name);
  vec_free (fds);
}

void
sapi_socket_close_w_handle (u32 api_handle)
{
  app_namespace_t *app_ns = app_namespace_get (api_handle >> 16);
  u16 sock_index = api_handle & 0xffff;
  app_ns_api_handle_t *handle;
  clib_socket_t *cs;
  clib_file_t *cf;

  cs = appns_sapi_get_socket (app_ns, sock_index);
  if (!cs)
    return;

  handle = (app_ns_api_handle_t *) & cs->private_data;
  cf = clib_file_get (&file_main, handle->aah_file_index);
  clib_file_del (&file_main, cf);

  clib_socket_close (cs);
  appns_sapi_free_socket (app_ns, cs);
}

static void
sapi_add_del_worker_handler (app_namespace_t * app_ns,
			     clib_socket_t * cs,
			     app_sapi_worker_add_del_msg_t * mp)
{
  int rv = 0, fds[SESSION_N_FD_TYPE], n_fds = 0;
  app_sapi_worker_add_del_reply_msg_t *rmp;
  app_ns_api_handle_t *handle;
  app_sapi_msg_t msg = { 0 };
  app_worker_t *app_wrk;
  u32 sapi_handle = -1;
  application_t *app;
  u8 fd_flags = 0;

  app = application_get_if_valid (mp->app_index);
  if (!app)
    {
      rv = SESSION_E_INVALID;
      goto done;
    }

  sapi_handle = appns_sapi_socket_handle (app_ns, cs);

  vnet_app_worker_add_del_args_t args = {
    .app_index = app->app_index,
    .wrk_map_index = mp->wrk_index,
    .api_client_index = sapi_handle,
    .is_add = mp->is_add
  };
  rv = vnet_app_worker_add_del (&args);
  if (rv)
    {
      clib_warning ("app worker add/del returned: %U", format_session_error,
		    rv);
      goto done;
    }

  if (!mp->is_add)
    goto done;

  /* Send fifo segment fd if needed */
  if (ssvm_type (args.segment) == SSVM_SEGMENT_MEMFD)
    {
      fd_flags |= SESSION_FD_F_MEMFD_SEGMENT;
      fds[n_fds] = args.segment->fd;
      n_fds += 1;
    }
  if (application_segment_manager_properties (app)->use_mq_eventfd)
    {
      fd_flags |= SESSION_FD_F_MQ_EVENTFD;
      fds[n_fds] = svm_msg_q_get_eventfd (args.evt_q);
      n_fds += 1;
    }

done:

  /* With app sock api socket expected to be closed, no reply */
  if (!mp->is_add && appns_sapi_enabled ())
    return;

  msg.type = APP_SAPI_MSG_TYPE_ADD_DEL_WORKER_REPLY;
  rmp = &msg.worker_add_del_reply;
  rmp->retval = rv;
  rmp->is_add = mp->is_add;
  rmp->wrk_index = mp->wrk_index;
  rmp->api_client_handle = sapi_handle;
  if (!rv && mp->is_add)
    {
      rmp->wrk_index = args.wrk_map_index;
      rmp->segment_handle = args.segment_handle;
      /* No segment name and size. This supports only memfds */
      rmp->app_event_queue_address =
	fifo_segment_msg_q_offset ((fifo_segment_t *) args.segment, 0);
      rmp->n_fds = n_fds;
      rmp->fd_flags = fd_flags;

      /* Update app index for socket */
      handle = (app_ns_api_handle_t *) & cs->private_data;
      app_wrk = application_get_worker (app, args.wrk_map_index);
      handle->aah_app_wrk_index = app_wrk->wrk_index;
    }

  clib_socket_sendmsg (cs, &msg, sizeof (msg), fds, n_fds);
}

/* This is a workaround for the case when session layer starts reading
 * the socket before the client actualy sends the data
 */
static clib_error_t *
sapi_socket_receive_wait (clib_socket_t *cs, u8 *msg, u32 msg_len)
{
  clib_error_t *err;
  int n_tries = 5;

  while (1)
    {
      err = clib_socket_recvmsg (cs, msg, msg_len, 0, 0);
      if (!err)
	break;

      if (!n_tries)
	return err;

      n_tries--;
      usleep (1);
    }

  return err;
}

static void
sapi_add_del_cert_key_handler (app_namespace_t *app_ns, clib_socket_t *cs,
			       app_sapi_cert_key_add_del_msg_t *mp)
{
  vnet_app_add_cert_key_pair_args_t _a, *a = &_a;
  app_sapi_cert_key_add_del_reply_msg_t *rmp;
  app_sapi_msg_t msg = { 0 };
  int rv = 0;

  if (mp->is_add)
    {
      const u32 max_certkey_len = 2e4, max_cert_len = 1e4, max_key_len = 1e4;
      clib_error_t *err;
      u8 *certkey = 0;
      u32 key_len;

      if (mp->certkey_len > max_certkey_len)
	{
	  rv = SESSION_E_INVALID;
	  goto send_reply;
	}

      vec_validate (certkey, mp->certkey_len - 1);

      err = sapi_socket_receive_wait (cs, certkey, mp->certkey_len);
      if (err)
	{
	  clib_error_report (err);
	  rv = SESSION_E_INVALID;
	  goto send_reply;
	}

      if (mp->cert_len > max_cert_len)
	{
	  rv = SESSION_E_INVALID;
	  goto send_reply;
	}

      if (mp->certkey_len < mp->cert_len)
	{
	  rv = SESSION_E_INVALID;
	  goto send_reply;
	}

      key_len = mp->certkey_len - mp->cert_len;
      if (key_len > max_key_len)
	{
	  rv = SESSION_E_INVALID;
	  goto send_reply;
	}

      clib_memset (a, 0, sizeof (*a));
      a->cert = certkey;
      a->key = certkey + mp->cert_len;
      a->cert_len = mp->cert_len;
      a->key_len = key_len;
      rv = vnet_app_add_cert_key_pair (a);

      vec_free (certkey);
    }
  else
    {
      rv = vnet_app_del_cert_key_pair (mp->index);
    }

send_reply:

  msg.type = APP_SAPI_MSG_TYPE_ADD_DEL_CERT_KEY_REPLY;
  rmp = &msg.cert_key_add_del_reply;
  rmp->retval = rv;
  rmp->context = mp->context;
  if (!rv && mp->is_add)
    rmp->index = a->index;

  clib_socket_sendmsg (cs, &msg, sizeof (msg), 0, 0);
}

static void
sapi_socket_detach (app_namespace_t * app_ns, clib_socket_t * cs)
{
  app_ns_api_handle_t *handle;
  app_worker_t *app_wrk;
  u32 api_client_handle;

  api_client_handle = appns_sapi_socket_handle (app_ns, cs);

  /* Cleanup everything because app worker closed socket or crashed */
  handle = (app_ns_api_handle_t *) & cs->private_data;
  app_wrk = app_worker_get_if_valid (handle->aah_app_wrk_index);
  if (!app_wrk)
    return;

  vnet_app_worker_add_del_args_t args = {
    .app_index = app_wrk->app_index,
    .wrk_map_index = app_wrk->wrk_map_index,
    .api_client_index = api_client_handle,
    .is_add = 0
  };
  /* Send rpc to main thread for worker barrier */
  vlib_rpc_call_main_thread (vnet_app_worker_add_del, (u8 *) & args,
			     sizeof (args));
}

static clib_error_t *
sapi_sock_read_ready (clib_file_t * cf)
{
  app_ns_api_handle_t *handle = (app_ns_api_handle_t *) & cf->private_data;
  vlib_main_t *vm = vlib_get_main ();
  app_sapi_msg_t msg = { 0 };
  app_namespace_t *app_ns;
  clib_error_t *err = 0;
  clib_socket_t *cs;

  app_ns = app_namespace_get (handle->aah_app_ns_index);
  cs = appns_sapi_get_socket (app_ns, handle->aah_sock_index);
  if (!cs)
    goto error;

  err = clib_socket_recvmsg (cs, &msg, sizeof (msg), 0, 0);
  if (err)
    {
      clib_error_free (err);
      sapi_socket_detach (app_ns, cs);
      goto error;
    }

  handle = (app_ns_api_handle_t *) & cs->private_data;

  vlib_worker_thread_barrier_sync (vm);

  switch (msg.type)
    {
    case APP_SAPI_MSG_TYPE_ATTACH:
      session_api_attach_handler (app_ns, cs, &msg.attach);
      break;
    case APP_SAPI_MSG_TYPE_ADD_DEL_WORKER:
      sapi_add_del_worker_handler (app_ns, cs, &msg.worker_add_del);
      break;
    case APP_SAPI_MSG_TYPE_ADD_DEL_CERT_KEY:
      sapi_add_del_cert_key_handler (app_ns, cs, &msg.cert_key_add_del);
      break;
    default:
      clib_warning ("app wrk %u unknown message type: %u",
		    handle->aah_app_wrk_index, msg.type);
      break;
    }

  vlib_worker_thread_barrier_release (vm);

error:
  return 0;
}

static clib_error_t *
sapi_sock_write_ready (clib_file_t * cf)
{
  app_ns_api_handle_t *handle = (app_ns_api_handle_t *) & cf->private_data;
  clib_warning ("called for app ns %u", handle->aah_app_ns_index);
  return 0;
}

static clib_error_t *
sapi_sock_error (clib_file_t * cf)
{
  app_ns_api_handle_t *handle = (app_ns_api_handle_t *) & cf->private_data;
  app_namespace_t *app_ns;
  clib_socket_t *cs;

  app_ns = app_namespace_get (handle->aah_app_ns_index);
  cs = appns_sapi_get_socket (app_ns, handle->aah_sock_index);
  if (!cs)
    return 0;

  sapi_socket_detach (app_ns, cs);
  return 0;
}

static clib_error_t *
sapi_sock_accept_ready (clib_file_t * scf)
{
  app_ns_api_handle_t handle = *(app_ns_api_handle_t *) & scf->private_data;
  app_namespace_t *app_ns;
  clib_file_t cf = { 0 };
  clib_error_t *err = 0;
  clib_socket_t *ccs, *scs;

  /* Listener files point to namespace */
  app_ns = app_namespace_get (handle.aah_app_ns_index);

  /*
   * Initialize client socket
   */
  ccs = appns_sapi_alloc_socket (app_ns);

  /* Grab server socket after client is initialized  */
  scs = appns_sapi_get_socket (app_ns, handle.aah_sock_index);
  if (!scs)
    goto error;

  err = clib_socket_accept (scs, ccs);
  if (err)
    {
      clib_error_report (err);
      goto error;
    }

  cf.read_function = sapi_sock_read_ready;
  cf.write_function = sapi_sock_write_ready;
  cf.error_function = sapi_sock_error;
  cf.file_descriptor = ccs->fd;
  /* File points to app namespace and socket */
  handle.aah_sock_index = appns_sapi_socket_index (app_ns, ccs);
  cf.private_data = handle.as_u64;
  cf.description = format (0, "app sock conn fd: %d", ccs->fd);

  /* Poll until we get an attach message. Socket points to file and
   * application that owns the socket */
  handle.aah_app_wrk_index = APP_INVALID_INDEX;
  handle.aah_file_index = clib_file_add (&file_main, &cf);
  ccs->private_data = handle.as_u64;

  return err;

error:
  appns_sapi_free_socket (app_ns, ccs);
  return err;
}

void
appns_sapi_del_ns_socket (app_namespace_t *app_ns)
{
  app_ns_api_handle_t *handle;
  clib_socket_t *cs;

  pool_foreach (cs, app_ns->app_sockets)
    {
      handle = (app_ns_api_handle_t *) &cs->private_data;
      clib_file_del_by_index (&file_main, handle->aah_file_index);

      clib_socket_close (cs);
      clib_socket_free (cs);
    }
  pool_free (app_ns->app_sockets);
}

int
appns_sapi_add_ns_socket (app_namespace_t * app_ns)
{
  char *subdir = "/app_ns_sockets/";
  app_ns_api_handle_t *handle;
  clib_file_t cf = { 0 };
  struct stat file_stat;
  clib_error_t *err;
  clib_socket_t *cs;
  char dir[4096];

  snprintf (dir, sizeof (dir), "%s%s", vlib_unix_get_runtime_dir (), subdir);

  if (!app_ns->sock_name)
    app_ns->sock_name = format (0, "%s%v%c", dir, app_ns->ns_id, 0);

  /*
   * Create and initialize socket to listen on
   */
  cs = appns_sapi_alloc_socket (app_ns);
  cs->config = (char *) vec_dup (app_ns->sock_name);
  cs->flags = CLIB_SOCKET_F_IS_SERVER |
    CLIB_SOCKET_F_ALLOW_GROUP_WRITE |
    CLIB_SOCKET_F_SEQPACKET | CLIB_SOCKET_F_PASSCRED;

  if (clib_socket_prefix_get_type (cs->config) == CLIB_SOCKET_TYPE_UNIX)
    {
      err = vlib_unix_recursive_mkdir ((char *) dir);
      if (err)
	{
	  clib_error_report (err);
	  return SESSION_E_SYSCALL;
	}
    }

  if ((err = clib_socket_init (cs)))
    {
      clib_error_report (err);
      return -1;
    }

  if (clib_socket_prefix_get_type (cs->config) == CLIB_SOCKET_TYPE_UNIX &&
      stat ((char *) app_ns->sock_name, &file_stat) == -1)
    return -1;

  /*
   * Start polling it
   */
  cf.read_function = sapi_sock_accept_ready;
  cf.file_descriptor = cs->fd;
  /* File points to namespace */
  handle = (app_ns_api_handle_t *) & cf.private_data;
  handle->aah_app_ns_index = app_namespace_index (app_ns);
  handle->aah_sock_index = appns_sapi_socket_index (app_ns, cs);
  cf.description = format (0, "app sock listener: %s", app_ns->sock_name);

  /* Socket points to clib file index */
  handle = (app_ns_api_handle_t *) & cs->private_data;
  handle->aah_file_index = clib_file_add (&file_main, &cf);
  handle->aah_app_wrk_index = APP_INVALID_INDEX;

  return 0;
}

#include <vnet/session/session.api.c>
static clib_error_t *
session_api_hookup (vlib_main_t *vm)
{
  api_main_t *am = vlibapi_get_main ();

  /*
   * Set up the (msg_name, crc, message-id) table
   */
  REPLY_MSG_ID_BASE = setup_message_id_table ();

  vl_api_set_msg_thread_safe (
    am, REPLY_MSG_ID_BASE + VL_API_SESSION_SDL_ADD_DEL, 1);
  vl_api_set_msg_thread_safe (
    am, REPLY_MSG_ID_BASE + VL_API_SESSION_SDL_ADD_DEL_REPLY, 1);
  vl_api_set_msg_thread_safe (am, REPLY_MSG_ID_BASE + VL_API_SESSION_SDL_DUMP,
			      1);
  vl_api_set_msg_thread_safe (
    am, REPLY_MSG_ID_BASE + VL_API_SESSION_SDL_DETAILS, 1);
  return 0;
}

VLIB_API_INIT_FUNCTION (session_api_hookup);

/*
 * fd.io coding-style-patch-verification: ON
 *
 * Local Variables:
 * eval: (c-set-style "gnu")
 * End:
 */