Line data Source code
1 : /*
2 : * nh.c
3 : *
4 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
5 : */
6 :
7 : #include <stdio.h>
8 : #include <stdlib.h>
9 : #include <unistd.h>
10 : #include <string.h>
11 : #include <errno.h>
12 : #include <stdlib.h>
13 : #include <stdbool.h>
14 : #include <getopt.h>
15 :
16 : #include <sys/types.h>
17 : #include <sys/socket.h>
18 :
19 : #include <net/if.h>
20 :
21 : #if defined(__linux__)
22 : #include <netinet/ether.h>
23 : #endif
24 :
25 : #include "vr_types.h"
26 : #include "vr_nexthop.h"
27 : #include "vr_os.h"
28 : #include "nl_util.h"
29 : #include "ini_parser.h"
30 :
31 : static int8_t src_mac[3][6], dst_mac[3][6], l3_vxlan_mac[6];
32 : static uint16_t sport, dport;
33 : static uint32_t nh_id, if_id[3] = {-1, -1, -1}, vrf_id, flags;
34 : static int nh_set, command, type, dump_marker = -1;
35 : static int family = AF_INET, count = 0;
36 :
37 : static bool dump_pending = false;
38 : static int comp_nh[32], lbl[32];
39 : static int comp_nh_ind = 0, lbl_ind = 0;
40 :
41 : static struct in_addr sip, dip;
42 : static struct nl_client *cl;
43 :
44 : static int
45 : vr_nh_op(struct nl_client *cl, int command, int type, uint32_t nh_id,
46 : uint32_t *if_id, uint32_t vrf_id, int8_t dst[][6], int8_t src[][6],
47 : struct in_addr sip, struct in_addr dip, uint32_t flags);
48 :
49 : char *
50 4 : nh_type(uint32_t type)
51 : {
52 4 : switch (type) {
53 0 : case NH_DEAD:
54 0 : return "Dead";
55 :
56 0 : case NH_RCV:
57 0 : return "Receive";
58 :
59 0 : case NH_L2_RCV:
60 0 : return "L2 Receive";
61 :
62 0 : case NH_ENCAP:
63 0 : return "Encap";
64 :
65 4 : case NH_TUNNEL:
66 4 : return "Tunnel";
67 :
68 0 : case NH_DISCARD:
69 0 : return "Drop";
70 :
71 0 : case NH_RESOLVE:
72 0 : return "Resolve";
73 :
74 0 : case NH_COMPOSITE:
75 0 : return "Composite";
76 :
77 0 : case NH_VRF_TRANSLATE:
78 0 : return "Vrf_Translate";
79 :
80 0 : default:
81 0 : return "Invalid";
82 : }
83 :
84 : return NULL;
85 : }
86 :
87 : char *
88 0 : nh_ecmp_config_hash_str(uint8_t hash, char *ptr)
89 : {
90 : int i;
91 :
92 0 : strcpy(ptr,"");
93 0 : hash = hash & ((1 << NH_ECMP_CONFIG_HASH_BITS) - 1);
94 0 : for (i = 0; i < NH_ECMP_CONFIG_HASH_BITS; i++) {
95 0 : switch (hash & (1 << i)) {
96 0 : case 0:
97 0 : break;
98 0 : case NH_ECMP_CONFIG_HASH_PROTO:
99 0 : strcat(ptr, "Proto,");
100 0 : break;
101 0 : case NH_ECMP_CONFIG_HASH_SRC_IP:
102 0 : strcat(ptr, "SrcIP,");
103 0 : break;
104 0 : case NH_ECMP_CONFIG_HASH_SRC_PORT:
105 0 : strcat(ptr, "SrcPort,");
106 0 : break;
107 0 : case NH_ECMP_CONFIG_HASH_DST_IP:
108 0 : strcat(ptr, "DstIp,");
109 0 : break;
110 0 : case NH_ECMP_CONFIG_HASH_DST_PORT:
111 0 : strcat(ptr, "DstPort");
112 0 : break;
113 0 : default:
114 0 : strcat(ptr, "Invalid,");
115 0 : break;
116 : }
117 : }
118 :
119 0 : return ptr;
120 : }
121 :
122 : char *
123 4 : nh_flags(uint32_t flags, uint8_t type, char *ptr)
124 : {
125 : int i;
126 : uint32_t mask;
127 :
128 4 : if (!flags) {
129 0 : strcpy(ptr, "None");
130 0 : return ptr;
131 : }
132 :
133 :
134 4 : strcpy(ptr,"");
135 132 : for (i = 0, mask = 1; (i < 32); i++, mask = mask << 1) {
136 128 : switch(flags & mask) {
137 4 : case NH_FLAG_VALID:
138 4 : strcat(ptr, "Valid, ");
139 4 : break;
140 :
141 0 : case NH_FLAG_POLICY_ENABLED:
142 0 : strcat(ptr, "Policy, ");
143 0 : break;
144 :
145 0 : case NH_FLAG_RELAXED_POLICY:
146 0 : strcat(ptr, "Policy(R), ");
147 0 : break;
148 :
149 0 : case NH_FLAG_FLOW_LOOKUP:
150 0 : strcat(ptr, "Flow Lookup, ");
151 0 : break;
152 :
153 0 : case NH_FLAG_TUNNEL_GRE:
154 0 : if (type == NH_TUNNEL) {
155 0 : if (flags & NH_FLAG_TUNNEL_MPLS_O_MPLS) {
156 0 : strcat(ptr, "MPLSo");
157 : }
158 0 : strcat(ptr, "MPLSoGRE, ");
159 : }
160 0 : break;
161 :
162 4 : case NH_FLAG_TUNNEL_UDP_MPLS:
163 4 : if (type == NH_TUNNEL) {
164 4 : if (flags & NH_FLAG_TUNNEL_MPLS_O_MPLS) {
165 0 : strcat(ptr, "MPLSo");
166 : }
167 4 : strcat(ptr, "MPLSoUDP, ");
168 : }
169 4 : break;
170 :
171 0 : case NH_FLAG_TUNNEL_UDP:
172 0 : if (type == NH_TUNNEL)
173 0 : strcat(ptr, "Udp, ");
174 0 : break;
175 :
176 0 : case NH_FLAG_COMPOSITE_ECMP:
177 0 : if (type == NH_COMPOSITE)
178 0 : strcat(ptr, "Ecmp, ");
179 0 : break;
180 :
181 0 : case NH_FLAG_COMPOSITE_FABRIC:
182 0 : if (type == NH_COMPOSITE)
183 0 : strcat(ptr, "Fabric, ");
184 0 : break;
185 :
186 0 : case NH_FLAG_COMPOSITE_EVPN:
187 0 : if (type == NH_COMPOSITE)
188 0 : strcat(ptr, "Evpn, ");
189 0 : break;
190 0 : case NH_FLAG_COMPOSITE_TOR:
191 0 : if (type == NH_COMPOSITE)
192 0 : strcat(ptr, "Tor, ");
193 0 : break;
194 :
195 0 : case NH_FLAG_COMPOSITE_ENCAP:
196 0 : if (type == NH_COMPOSITE)
197 0 : strcat(ptr, "Encap, ");
198 0 : break;
199 :
200 0 : case NH_FLAG_MCAST:
201 0 : strcat(ptr, "Multicast, ");
202 0 : break;
203 :
204 0 : case NH_FLAG_ROUTE_LOOKUP:
205 0 : strcat(ptr, "RouteLookup, ");
206 0 : break;
207 :
208 0 : case NH_FLAG_L3_VXLAN:
209 0 : strcat(ptr, "l3_vxlan, ");
210 0 : break;
211 :
212 0 : case NH_FLAG_TUNNEL_VXLAN:
213 0 : strcat(ptr, "Vxlan, ");
214 0 : break;
215 :
216 0 : case NH_FLAG_UNKNOWN_UC_FLOOD:
217 0 : strcat(ptr, "Unicast Flood, ");
218 0 : break;
219 :
220 0 : case NH_FLAG_TUNNEL_SIP_COPY:
221 0 : strcat(ptr, "Copy SIP, ");
222 0 : break;
223 :
224 0 : case NH_FLAG_TUNNEL_PBB:
225 0 : strcat(ptr, "Pbb, ");
226 0 : break;
227 :
228 0 : case NH_FLAG_INDIRECT:
229 0 : strcat(ptr, "Indirect, ");
230 0 : break;
231 :
232 4 : case NH_FLAG_ETREE_ROOT:
233 4 : strcat(ptr, "Etree Root, ");
234 4 : break;
235 :
236 0 : case NH_FLAG_MAC_LEARN:
237 0 : strcat(ptr, "Mac Learn, ");
238 0 : break;
239 :
240 0 : case NH_FLAG_L2_CONTROL_DATA:
241 0 : strcat(ptr, "Evpn Control Word, ");
242 0 : break;
243 :
244 0 : case NH_FLAG_CRYPT_TRAFFIC:
245 0 : strcat(ptr, "Encrypt Traffic, ");
246 0 : break;
247 :
248 4 : case NH_FLAG_TUNNEL_UNDERLAY_ECMP:
249 4 : strcat(ptr, "Underlay Ecmp, ");
250 4 : break;
251 : }
252 : }
253 :
254 4 : return ptr;
255 : }
256 :
257 : static void
258 20 : nh_print_newline_header(void)
259 : {
260 20 : printf("\n%14c", ' ');
261 20 : return;
262 : }
263 :
264 : static void
265 4 : nexthop_req_process(void *s_req)
266 : {
267 4 : unsigned int i, j, printed = 0;
268 : struct in_addr a;
269 : char flags_mem[500];
270 : char fam[100];
271 4 : char in6_dst[INET6_ADDRSTRLEN] = { 0 };
272 :
273 4 : vr_nexthop_req *req = (vr_nexthop_req *)(s_req);
274 :
275 4 : if (req->nhr_family == AF_INET)
276 4 : strcpy(fam, "AF_INET");
277 0 : else if (req->nhr_family == AF_INET6)
278 0 : strcpy(fam, "AF_INET6");
279 0 : else if (req->nhr_family == AF_BRIDGE)
280 0 : strcpy(fam, "AF_BRIDGE");
281 0 : else if (req->nhr_family == AF_UNSPEC)
282 0 : strcpy(fam, "AF_UNSPEC");
283 : else
284 0 : strcpy(fam, "N/A");
285 :
286 4 : printf("Id:%-9d Type:%-13s Fmly:%8s Rid:%d Ref_cnt:%-10d Vrf:%d",
287 4 : req->nhr_id, nh_type(req->nhr_type), fam,
288 : req->nhr_rid, req->nhr_ref_cnt, req->nhr_vrf);
289 4 : nh_print_newline_header();
290 4 : printf("Flags:%s",
291 4 : nh_flags(req->nhr_flags, req->nhr_type, flags_mem));
292 :
293 4 : if ((req->nhr_flags & NH_FLAG_INDIRECT) && (req->nhr_nh_list_size)) {
294 0 : i = -1;
295 0 : if (req->nhr_label_list_size)
296 0 : i = req->nhr_label_list[0];
297 0 : nh_print_newline_header();
298 0 : printf("Direct NH(label): %d(%d)", req->nhr_nh_list[0], i);
299 : }
300 :
301 4 : if (req->nhr_type == NH_RCV) {
302 0 : nh_print_newline_header();
303 0 : printf("Oif:%d", req->nhr_encap_oif_id[0]);
304 4 : } else if (req->nhr_type == NH_ENCAP) {
305 0 : nh_print_newline_header();
306 0 : printf("EncapFmly:%04x Oif:%d Len:%d", req->nhr_encap_family,
307 0 : req->nhr_encap_oif_id[0], req->nhr_encap_size);
308 0 : nh_print_newline_header();
309 0 : printf("Encap Data: ");
310 0 : for (i = 0; i< req->nhr_encap_size; i++) {
311 0 : printf("%02x ", (unsigned char)req->nhr_encap[i]);
312 : }
313 4 : } else if (req->nhr_type == NH_TUNNEL) {
314 4 : nh_print_newline_header();
315 4 : if (!(req->nhr_flags & NH_FLAG_TUNNEL_PBB)) {
316 4 : if (req->nhr_flags & NH_FLAG_TUNNEL_UNDERLAY_ECMP) {
317 16 : for (i = 0; i < req->nhr_encap_oif_id_size; i++) {
318 12 : printf("Oif:%d EncapValid:%d ",
319 12 : req->nhr_encap_oif_id[i], req->nhr_encap_valid[i]);
320 12 : if (req->nhr_encap_valid[i]) {
321 6 : printf("Len:%d Data:", req->nhr_encap_len);
322 90 : for (j = 0; j < req->nhr_encap_len; j++) {
323 84 : printf("%02x ",
324 84 : (unsigned char)req->nhr_encap[i*req->nhr_encap_len+j]);
325 : }
326 : } else
327 6 : printf("Len:0 Data:NULL");
328 12 : nh_print_newline_header();
329 : }
330 : } else {
331 0 : printf("Oif:%d Len:%d Data:",
332 0 : req->nhr_encap_oif_id[0], req->nhr_encap_size);
333 0 : for (i = 0; i< req->nhr_encap_size; i++) {
334 0 : printf("%02x ", (unsigned char)req->nhr_encap[i]);
335 : }
336 0 : nh_print_newline_header();
337 : }
338 : }
339 4 : if (!(req->nhr_flags & NH_FLAG_TUNNEL_PBB)) {
340 4 : if (req->nhr_family == AF_INET) {
341 4 : a.s_addr = req->nhr_tun_sip;
342 4 : printf("Sip:%s", inet_ntoa(a));
343 4 : a.s_addr = req->nhr_tun_dip;
344 4 : printf(" Dip:%s", inet_ntoa(a));
345 0 : } else if (req->nhr_family == AF_INET6) {
346 0 : printf("Sip: %s",
347 0 : inet_ntop(AF_INET6, (struct in6_addr *)req->nhr_tun_sip6,
348 : in6_dst, sizeof(in6_dst)));
349 0 : printf(" Dip: %s",
350 0 : inet_ntop(AF_INET6, (struct in6_addr *)req->nhr_tun_dip6,
351 : in6_dst, sizeof(in6_dst)));
352 : }
353 : }
354 :
355 4 : if (req->nhr_flags & NH_FLAG_TUNNEL_UDP) {
356 0 : nh_print_newline_header();
357 0 : printf("Sport:%d Dport:%d\n", ntohs(req->nhr_tun_sport),
358 0 : ntohs(req->nhr_tun_dport));
359 : }
360 :
361 4 : if (req->nhr_flags & NH_FLAG_L3_VXLAN)
362 0 : printf(" L3_Vxlan_Mac: "MAC_FORMAT,
363 0 : MAC_VALUE((uint8_t*)req->nhr_rw_dst_mac));
364 :
365 4 : if (req->nhr_flags & NH_FLAG_TUNNEL_PBB) {
366 0 : i = -1;
367 0 : if (req->nhr_label_list_size)
368 0 : i = req->nhr_label_list[0];
369 0 : printf("Bmac:"MAC_FORMAT " Label:%d",
370 0 : MAC_VALUE((uint8_t *)req->nhr_pbb_mac), i);
371 : }
372 4 : if(req->nhr_flags & NH_FLAG_TUNNEL_MPLS_O_MPLS) {
373 0 : printf(" Transport Label:%u", req->nhr_transport_label);
374 : }
375 4 : if (req->nhr_encap_crypt_oif_id != (int)-1 &&
376 4 : req->nhr_encap_crypt_oif_id != 0) {
377 0 : nh_print_newline_header();
378 0 : printf("CryptOif:%d\n", req->nhr_encap_crypt_oif_id);
379 : }
380 0 : } else if (req->nhr_type == NH_VRF_TRANSLATE) {
381 0 : nh_print_newline_header();
382 0 : printf("Vrf:%d", req->nhr_vrf);
383 0 : } else if (req->nhr_type == NH_COMPOSITE) {
384 0 : if (req->nhr_flags & NH_FLAG_COMPOSITE_ECMP) {
385 0 : if (req->nhr_ecmp_config_hash) {
386 0 : nh_print_newline_header();
387 0 : nh_ecmp_config_hash_str(req->nhr_ecmp_config_hash, flags_mem);
388 0 : printf("Valid Hash Key Parameters: %s", flags_mem);
389 : }
390 : }
391 0 : nh_print_newline_header();
392 0 : printf("Sub NH(label):");
393 0 : for (i = 0; i < req->nhr_nh_list_size; i++) {
394 0 : if (printed > 60) {
395 0 : nh_print_newline_header();
396 0 : printf("%14c", ' ');
397 0 : printed = 0;
398 : }
399 0 : printed += printf(" %d", req->nhr_nh_list[i]);
400 0 : if (req->nhr_label_list[i] >= 0)
401 0 : printed += printf("(%d)", req->nhr_label_list[i]);
402 : }
403 :
404 0 : if (req->nhr_nh_count &&
405 0 : (req->nhr_nh_count - req->nhr_nh_list_size)) {
406 0 : printf(" and %u more components...\n",
407 0 : req->nhr_nh_count - req->nhr_nh_list_size);
408 : }
409 : }
410 :
411 4 : if (command == SANDESH_OP_DUMP) {
412 0 : dump_marker = req->nhr_id;
413 : }
414 :
415 4 : printf("\n\n");
416 4 : if (command == SANDESH_OP_GET) {
417 4 : if (req->nhr_type == NH_COMPOSITE) {
418 0 : for (i = 0; i < req->nhr_nh_list_size; i++) {
419 : // Skip expanding sub-nh for -1 index
420 0 : if (req->nhr_nh_list[i] == -1)
421 0 : continue;
422 0 : vr_nh_op(cl, command, type, req->nhr_nh_list[i], if_id, vrf_id,
423 : dst_mac, src_mac, sip, dip, flags);
424 : }
425 : }
426 :
427 4 : if ((req->nhr_flags & NH_FLAG_INDIRECT) && (req->nhr_nh_list_size)) {
428 0 : vr_nh_op(cl, command, type, req->nhr_nh_list[0], if_id, vrf_id,
429 : dst_mac, src_mac, sip, dip, flags);
430 : }
431 : }
432 4 : }
433 :
434 : static void
435 4 : response_process(void *s)
436 : {
437 4 : vr_response_common_process((vr_response *)s, &dump_pending);
438 4 : return;
439 : }
440 :
441 : static void
442 4 : nh_fill_nl_callbacks()
443 : {
444 4 : nl_cb.vr_response_process = response_process;
445 4 : nl_cb.vr_nexthop_req_process = nexthop_req_process;
446 4 : }
447 :
448 : static int
449 4 : vr_nh_op(struct nl_client *cl, int command, int type, uint32_t nh_id,
450 : uint32_t *if_id, uint32_t vrf_id, int8_t dst[][6], int8_t src[][6],
451 : struct in_addr sip, struct in_addr dip, uint32_t flags)
452 : {
453 : int ret;
454 4 : bool dump = false;
455 :
456 4 : op_retry:
457 4 : switch (command) {
458 0 : case SANDESH_OP_ADD:
459 0 : if (flags & NH_FLAG_TUNNEL_PBB) {
460 0 : ret = vr_send_pbb_tunnel_add(cl, 0, nh_id, flags,
461 0 : vrf_id, dst[0], comp_nh[0], lbl[0]);
462 0 : } else if ((type == NH_ENCAP) || (type == NH_TUNNEL)) {
463 0 : ret = vr_send_nexthop_encap_tunnel_add(cl, 0, type, nh_id,
464 : flags, vrf_id, if_id, src, dst, sip, dip, sport, dport, l3_vxlan_mac, family, count);
465 0 : } else if (type == NH_COMPOSITE) {
466 0 : ret = vr_send_nexthop_composite_add(cl, 0, nh_id, flags, vrf_id,
467 : comp_nh_ind, comp_nh, lbl, family);
468 : } else {
469 0 : ret = vr_send_nexthop_add(cl, 0, type, nh_id, flags, vrf_id, if_id, family);
470 : }
471 :
472 0 : break;
473 :
474 0 : case SANDESH_OP_DEL:
475 0 : ret = vr_send_nexthop_delete(cl, 0, nh_id);
476 0 : break;
477 :
478 0 : case SANDESH_OP_DUMP:
479 0 : dump = true;
480 0 : ret = vr_send_nexthop_dump(cl, 0, dump_marker);
481 0 : break;
482 :
483 4 : case SANDESH_OP_GET:
484 4 : ret = vr_send_nexthop_get(cl, 0, nh_id);
485 4 : break;
486 :
487 0 : default:
488 0 : ret = -EINVAL;
489 : }
490 :
491 4 : if (ret < 0)
492 0 : return ret;
493 :
494 4 : ret = vr_recvmsg(cl, dump);
495 4 : if (ret <= 0)
496 0 : return ret;
497 :
498 4 : if (dump_pending)
499 0 : goto op_retry;
500 :
501 4 : return 0;
502 : }
503 :
504 : void
505 0 : cmd_usage()
506 : {
507 0 : printf("Usage: [--create <nhid> create nexthop\n"
508 : " [--delete <nhid> delete nexthop\n"
509 : " [--vrf <vrf_id> ]\n"
510 : " [--pol NH with policy]\n"
511 : " [--rpol NH with relaxed policy]\n"
512 : " [--l2 NH with family bridge]\n"
513 : " [--root NH is an Etree Root]\n"
514 : " [--rlkup Force Route Lookup]\n"
515 : " [--type <type> type of the tunnel 1 - rcv, 2 - encap \n"
516 : " 3 - tunnel, 4 - resolve, 5 - discard, 6 - Composite\n"
517 : " 7 - VRF Translate, 8 - L2 Rcv NH] \n"
518 : " [RCV_NH options]\n"
519 : " [--oif <if_id> out going interface index]\n"
520 : " [L2RCV_NH options]\n"
521 : " [--oif <if_id> out going interface index]\n"
522 : " [ENCAP_NH optionsi - default L3]\n"
523 : " [--mc multicast nh]\n"
524 : " [--smac <xx:xx:xx:xx:xx:xx> source mac ]\n"
525 : " [--dmac <xx:xx:xx:xx:xx:xx> destination mac ]\n"
526 : " [--oif = out going interface index]\n"
527 : " [TUNNEL_NH options - default Gre]\n"
528 : " [--pbb PBB tunnel options]\n"
529 : " [--cni <nh_id> direct nh member id]\n"
530 : " [--lbl <lbl> Evpn label for PBB tunnel]\n"
531 : " [--dmac <xx:xx:xx:xx:xx:xx> destination Bmac]\n"
532 : " [--ind indirect flag]\n"
533 : " [--oif <if_id1,if_id2,...> comma seperated list of out going interface indices(max 3 in case of L3 multihoming)]\n"
534 : " [--smac <xx:xx:xx:xx:xx:xx,xx:xx:xx:xx:xx:xx,...> comma seperated list of source mac(max 3 in case of L3 multihoming)]\n"
535 : " [--dmac <xx:xx:xx:xx:xx:xx,xx:xx:xx:xx:xx:xx,...> comma seperated list of destination mac(max 3 in case of L3 multihoming)]\n"
536 : " [--sip <x.x.x.x> source ip of tunnel] \n"
537 : " [--dip <x.x.x.x> destination ip of tunnel ]\n"
538 : " [--udp Udptunnel ]\n"
539 : " [--sport <port> source port of udp tunnel]\n"
540 : " [--dport <port> destination port of udp tunnel]\n"
541 : " [--vxlan Vxlan Tunnel]\n"
542 : " [--sport <port> source port of vxlan tunnel]\n"
543 : " [--dport <port> destination port of vxlan tunnel]\n"
544 : " [--l3_vxlan <xx:xx:xx:xx:xx:xx> Remote EVPN-5 vRouter mac]\n"
545 : " [RESOLVE_NH options]\n"
546 : " [DISCARD_NH options]\n"
547 : " [COMPOSITE_NH options]\n"
548 : " [--cni <nh_id> composite nexthop member id]\n"
549 : " [--cmc composite multicast nexhop]\n"
550 : " [--cfa composit fabric ]\n"
551 : " [--cen composit encap ]\n"
552 : " [--cevpn composit evpn ]\n"
553 : " [--lbl <lbl> label for composit fabric ]\n"
554 : " [--tor composit tor ]\n"
555 : " [--lbl <lbl> label for composit fabric ]\n"
556 : " [--ecmp composite ecmp nexthop]\n"
557 : " [VRF Translate options]\n"
558 : " [--vxlan Vxlan VRF Translation]\n"
559 : " [--uucf Unknown Unicast Flood]\n");
560 0 : exit(-EINVAL);
561 : }
562 :
563 : void
564 0 : usage()
565 : {
566 0 : printf("Usage: nh --list\n"
567 : " nh --get <nh_id>\n"
568 : " nh --help\n\n"
569 : "--list Lists All Nexthops\n"
570 : "--get <nh_id> Displays nexthop corresponding to <nh_id>\n"
571 : "--sock-dir <netlink sock dir>\n"
572 : "--help Displays this help message\n\n");
573 :
574 0 : exit(-EINVAL);
575 : }
576 :
577 : enum opt_index {
578 : OIF_OPT_IND,
579 : SMAC_OPT_IND,
580 : DMAC_OPT_IND,
581 : VRF_OPT_IND,
582 : TYPE_OPT_IND,
583 : SIP_OPT_IND,
584 : DIP_OPT_IND,
585 : POL_OPT_IND,
586 : RPOL_OPT_IND,
587 : L2_OPT_IND,
588 : SPORT_OPT_IND,
589 : DPORT_OPT_IND,
590 : L3_VXLAN_OPT_IND,
591 : UDP_OPT_IND,
592 : VXLAN_OPT_IND,
593 : CNI_OPT_IND,
594 : CMC_OPT_IND,
595 : CFA_OPT_IND,
596 : MC_OPT_IND,
597 : CEN_OPT_IND,
598 : CEVPN_OPT_IND,
599 : TOR_OPT_IND,
600 : RLKUP_OPT_IND,
601 : LBL_OPT_IND,
602 : UUCF_OPT_IND,
603 : LST_OPT_IND,
604 : GET_OPT_IND,
605 : CRT_OPT_IND,
606 : DEL_OPT_IND,
607 : CMD_OPT_IND,
608 : IND_OPT_IND,
609 : PBB_OPT_IND,
610 : ROOT_OPT_IND,
611 : ML_OPT_IND,
612 : HLP_OPT_IND,
613 : SOCK_DIR_OPT_IND,
614 : ECMP_OPT_IND,
615 : MAX_OPT_IND
616 : };
617 :
618 : static int opt[MAX_OPT_IND], zero_opt[MAX_OPT_IND];
619 :
620 : static bool
621 16 : opt_set(int ind)
622 : {
623 16 : if (ind < 0 || ind >= MAX_OPT_IND)
624 0 : return false;
625 :
626 16 : if (opt[ind]) {
627 8 : opt[ind] = 0;
628 8 : return true;
629 : }
630 :
631 8 : return false;
632 : }
633 :
634 : static struct option long_options[] = {
635 : [OIF_OPT_IND] = {"oif", required_argument, &opt[OIF_OPT_IND], 1},
636 : [SMAC_OPT_IND] = {"smac", required_argument, &opt[SMAC_OPT_IND], 1},
637 : [DMAC_OPT_IND] = {"dmac", required_argument, &opt[DMAC_OPT_IND], 1},
638 : [VRF_OPT_IND] = {"vrf", required_argument, &opt[VRF_OPT_IND], 1},
639 : [TYPE_OPT_IND] = {"type", required_argument, &opt[TYPE_OPT_IND], 1},
640 : [SIP_OPT_IND] = {"sip", required_argument, &opt[SIP_OPT_IND], 1},
641 : [DIP_OPT_IND] = {"dip", required_argument, &opt[DIP_OPT_IND], 1},
642 : [POL_OPT_IND] = {"pol", no_argument, &opt[POL_OPT_IND], 1},
643 : [RPOL_OPT_IND] = {"rpol", no_argument, &opt[RPOL_OPT_IND], 1},
644 : [L2_OPT_IND] = {"l2", no_argument, &opt[L2_OPT_IND], 1},
645 : [SPORT_OPT_IND] = {"sport", required_argument, &opt[SPORT_OPT_IND], 1},
646 : [DPORT_OPT_IND] = {"dport", required_argument, &opt[DPORT_OPT_IND], 1},
647 : [L3_VXLAN_OPT_IND] = {"l3_vxlan", required_argument, &opt[L3_VXLAN_OPT_IND], 1},
648 : [UDP_OPT_IND] = {"udp", no_argument, &opt[UDP_OPT_IND], 1},
649 : [VXLAN_OPT_IND] = {"vxlan", no_argument, &opt[VXLAN_OPT_IND], 1},
650 : [CNI_OPT_IND] = {"cni", required_argument, &opt[CNI_OPT_IND], 1},
651 : [CMC_OPT_IND] = {"cmc", no_argument, &opt[CMC_OPT_IND], 1},
652 : [CFA_OPT_IND] = {"cfa", no_argument, &opt[CFA_OPT_IND], 1},
653 : [MC_OPT_IND] = {"mc", no_argument, &opt[MC_OPT_IND], 1},
654 : [CEN_OPT_IND] = {"cen", no_argument, &opt[CEN_OPT_IND], 1},
655 : [CEVPN_OPT_IND] = {"cevpn", no_argument, &opt[CEVPN_OPT_IND], 1},
656 : [TOR_OPT_IND] = {"tor", no_argument, &opt[TOR_OPT_IND], 1},
657 : [RLKUP_OPT_IND] = {"rlkup", no_argument, &opt[RLKUP_OPT_IND], 1},
658 : [LBL_OPT_IND] = {"lbl", required_argument, &opt[LBL_OPT_IND], 1},
659 : [UUCF_OPT_IND] = {"uucf", no_argument, &opt[UUCF_OPT_IND], 1},
660 : [LST_OPT_IND] = {"list", no_argument, &opt[LST_OPT_IND], 1},
661 : [GET_OPT_IND] = {"get", required_argument, &opt[GET_OPT_IND], 1},
662 : [CRT_OPT_IND] = {"create", required_argument, &opt[CRT_OPT_IND], 1},
663 : [DEL_OPT_IND] = {"delete", required_argument, &opt[DEL_OPT_IND], 1},
664 : [CMD_OPT_IND] = {"cmd", no_argument, &opt[CMD_OPT_IND], 1},
665 : [IND_OPT_IND] = {"ind", no_argument, &opt[IND_OPT_IND], 1},
666 : [PBB_OPT_IND] = {"pbb", no_argument, &opt[PBB_OPT_IND], 1},
667 : [ROOT_OPT_IND] = {"root", no_argument, &opt[ROOT_OPT_IND], 1},
668 : [ML_OPT_IND] = {"ml", no_argument, &opt[ML_OPT_IND], 1},
669 : [HLP_OPT_IND] = {"help", no_argument, &opt[HLP_OPT_IND], 1},
670 : [SOCK_DIR_OPT_IND] = {"sock-dir", required_argument, &opt[SOCK_DIR_OPT_IND], 1},
671 : [ECMP_OPT_IND] = {"ecmp", no_argument, &opt[ECMP_OPT_IND], 1},
672 : [MAX_OPT_IND] = { NULL, 0, 0, 0}
673 : };
674 :
675 : static void
676 8 : parse_long_opts(int ind, char *opt_arg)
677 : {
678 : int errno, i;
679 : char *c;
680 : struct ether_addr *mac;
681 :
682 8 : errno = 0;
683 8 : switch (ind) {
684 0 : case CMD_OPT_IND:
685 0 : cmd_usage();
686 0 : break;
687 :
688 0 : case HLP_OPT_IND:
689 0 : usage();
690 0 : break;
691 :
692 4 : case GET_OPT_IND:
693 : case CRT_OPT_IND:
694 : case DEL_OPT_IND:
695 4 : nh_id = strtoul(opt_arg, NULL, 0);
696 4 : if (errno)
697 0 : usage();
698 4 : nh_set = 1;
699 4 : break;
700 :
701 0 : case OIF_OPT_IND:
702 0 : count = 0;
703 0 : i = 0;
704 0 : c = strtok(opt_arg, ",");
705 0 : while (c != NULL) {
706 0 : if_id[i++] = atoi(c);
707 0 : c = strtok(NULL, ",");
708 : }
709 0 : count = i;
710 0 : if (errno)
711 0 : usage();
712 0 : break;
713 :
714 0 : case SMAC_OPT_IND:
715 0 : c = NULL;
716 0 : i = 0;
717 0 : c = strtok(opt_arg, ",");
718 0 : while (c != NULL) {
719 0 : mac = ether_aton(c);
720 0 : if (mac)
721 0 : memcpy(src_mac[i], mac, sizeof(src_mac[i]));
722 : else
723 0 : cmd_usage();
724 0 : c = strtok(NULL, ",");
725 0 : i++;
726 0 : mac = NULL;
727 : }
728 0 : break;
729 :
730 0 : case DMAC_OPT_IND:
731 0 : c = NULL;
732 0 : i = 0;
733 0 : c = strtok(opt_arg, ",");
734 0 : while (c != NULL) {
735 0 : mac = ether_aton(c);
736 0 : if (mac)
737 0 : memcpy(dst_mac[i], mac, sizeof(dst_mac[i]));
738 : else
739 0 : cmd_usage();
740 0 : c = strtok(NULL, ",");
741 0 : i++;
742 0 : mac = NULL;
743 : }
744 0 : break;
745 :
746 0 : case VRF_OPT_IND:
747 0 : vrf_id = strtoul(opt_arg, NULL, 0);
748 0 : if (errno)
749 0 : usage();
750 0 : break;
751 :
752 0 : case TYPE_OPT_IND:
753 0 : type = strtoul(opt_arg, NULL, 0);
754 0 : if (errno)
755 0 : usage();
756 0 : break;
757 :
758 0 : case SIP_OPT_IND:
759 0 : inet_aton(opt_arg, &sip);
760 0 : break;
761 :
762 0 : case DIP_OPT_IND:
763 0 : inet_aton(opt_arg, &dip);
764 0 : break;
765 :
766 0 : case SPORT_OPT_IND:
767 0 : sport = strtoul(opt_arg, NULL, 0);
768 0 : if (errno)
769 0 : usage();
770 0 : break;
771 :
772 0 : case CNI_OPT_IND:
773 0 : comp_nh[comp_nh_ind++] = strtoul(opt_arg, NULL, 0);
774 0 : if (errno)
775 0 : usage();
776 0 : break;
777 :
778 0 : case LBL_OPT_IND:
779 0 : lbl[lbl_ind++] = strtoul(opt_arg, NULL, 0);
780 0 : if (errno)
781 0 : usage();
782 0 : break;
783 :
784 0 : case DPORT_OPT_IND:
785 0 : dport = strtoul(opt_arg, NULL, 0);
786 0 : if (errno)
787 0 : usage();
788 0 : break;
789 0 : case L3_VXLAN_OPT_IND:
790 0 : mac = ether_aton(opt_arg);
791 0 : if (mac)
792 0 : memcpy(l3_vxlan_mac, mac, sizeof(l3_vxlan_mac));
793 : else
794 0 : cmd_usage();
795 0 : break;
796 4 : case SOCK_DIR_OPT_IND:
797 4 : vr_socket_dir = opt_arg;
798 4 : break;
799 : }
800 :
801 8 : return;
802 : }
803 :
804 : static void
805 4 : validate_options(void)
806 : {
807 4 : if (opt_set(CRT_OPT_IND)) {
808 0 : command = SANDESH_OP_ADD;
809 4 : } else if (opt_set(DEL_OPT_IND)) {
810 0 : command = SANDESH_OP_DEL;
811 4 : } else if (opt_set(GET_OPT_IND)) {
812 4 : command = SANDESH_OP_GET;
813 0 : } else if (opt_set(LST_OPT_IND)) {
814 0 : command = SANDESH_OP_DUMP;
815 : } else {
816 0 : usage();
817 0 : return;
818 : }
819 :
820 :
821 4 : switch (command) {
822 0 : case SANDESH_OP_ADD:
823 0 : if (!nh_set)
824 0 : cmd_usage();
825 :
826 0 : flags |= NH_FLAG_VALID;
827 0 : if (!opt_set(TYPE_OPT_IND))
828 0 : cmd_usage();
829 :
830 0 : if(!opt_set(VRF_OPT_IND))
831 0 : cmd_usage();
832 :
833 0 : if (opt_set(MC_OPT_IND))
834 0 : flags |= NH_FLAG_MCAST;
835 :
836 0 : if (opt_set(POL_OPT_IND))
837 0 : flags |= NH_FLAG_POLICY_ENABLED;
838 :
839 0 : if (opt_set(IND_OPT_IND))
840 0 : flags |= NH_FLAG_INDIRECT;
841 :
842 0 : if (opt_set(RPOL_OPT_IND))
843 0 : flags |= NH_FLAG_RELAXED_POLICY;
844 :
845 0 : if (opt_set(L2_OPT_IND))
846 0 : family = AF_BRIDGE;
847 :
848 0 : if (opt_set(RLKUP_OPT_IND))
849 0 : flags |= NH_FLAG_ROUTE_LOOKUP;
850 :
851 0 : if (opt_set(ML_OPT_IND))
852 0 : flags |= NH_FLAG_MAC_LEARN;
853 :
854 0 : if (opt_set(ROOT_OPT_IND))
855 0 : flags |= NH_FLAG_ETREE_ROOT;
856 :
857 0 : if (type == NH_RCV) {
858 0 : if (!opt_set(OIF_OPT_IND))
859 0 : cmd_usage();
860 :
861 0 : if (memcmp(opt, zero_opt, sizeof(opt)))
862 0 : cmd_usage();
863 0 : } else if (type == NH_L2_RCV) {
864 0 : if (memcmp(opt, zero_opt, sizeof(opt)))
865 0 : cmd_usage();
866 0 : } else if (type == NH_ENCAP) {
867 0 : if (!opt_set(OIF_OPT_IND))
868 0 : cmd_usage();
869 :
870 0 : if (family == AF_INET) {
871 0 : if (!opt_set(SMAC_OPT_IND) || !opt_set(DMAC_OPT_IND))
872 0 : cmd_usage();
873 :
874 0 : if (opt_set(L3_VXLAN_OPT_IND))
875 0 : flags |= NH_FLAG_L3_VXLAN;
876 :
877 0 : if (memcmp(opt, zero_opt, sizeof(opt)))
878 0 : cmd_usage();
879 : }
880 :
881 0 : } else if (type == NH_TUNNEL) {
882 :
883 0 : if (count > 1)
884 0 : flags |= NH_FLAG_TUNNEL_UNDERLAY_ECMP;
885 :
886 0 : if (opt_set(PBB_OPT_IND)) {
887 0 : if (!opt_set(CNI_OPT_IND)) {
888 0 : cmd_usage();
889 : }
890 :
891 0 : if (comp_nh_ind != 1)
892 0 : cmd_usage();
893 :
894 0 : if (!opt_set(LBL_OPT_IND) || !opt_set(DMAC_OPT_IND))
895 0 : cmd_usage();
896 :
897 0 : flags |= NH_FLAG_TUNNEL_PBB;
898 :
899 0 : } else if (!opt_set(OIF_OPT_IND) || !opt_set(SMAC_OPT_IND) ||
900 0 : !opt_set(DMAC_OPT_IND) || !opt_set(SIP_OPT_IND) ||
901 0 : !opt_set(DIP_OPT_IND)) {
902 0 : cmd_usage();
903 : }
904 :
905 0 : if (opt_set(UDP_OPT_IND)) {
906 0 : if (!opt_set(SPORT_OPT_IND) || !opt_set(DPORT_OPT_IND))
907 0 : flags |= NH_FLAG_TUNNEL_UDP_MPLS;
908 : else
909 0 : flags |= NH_FLAG_TUNNEL_UDP;
910 0 : } else if (opt_set(VXLAN_OPT_IND)) {
911 0 : flags |= NH_FLAG_TUNNEL_VXLAN;
912 0 : if (!opt_set(SPORT_OPT_IND) || !opt_set(DPORT_OPT_IND))
913 0 : cmd_usage();
914 0 : if (opt_set(L3_VXLAN_OPT_IND))
915 0 : flags |= NH_FLAG_L3_VXLAN;
916 : }
917 :
918 0 : if (!(flags & (NH_FLAG_TUNNEL_UDP_MPLS | NH_FLAG_TUNNEL_UDP |
919 : NH_FLAG_TUNNEL_VXLAN | NH_FLAG_TUNNEL_PBB)))
920 0 : flags |= NH_FLAG_TUNNEL_GRE;
921 :
922 0 : if (memcmp(opt, zero_opt, sizeof(opt)))
923 0 : cmd_usage();
924 0 : } else if (type == NH_RESOLVE) {
925 0 : if (memcmp(opt, zero_opt, sizeof(opt)))
926 0 : cmd_usage();
927 0 : } else if (type == NH_DISCARD) {
928 0 : if (memcmp(opt, zero_opt, sizeof(opt)))
929 0 : cmd_usage();
930 0 : } else if (type == NH_COMPOSITE) {
931 0 : if (!opt_set(CNI_OPT_IND))
932 0 : cmd_usage();
933 :
934 0 : if (opt_set(CMC_OPT_IND)) {
935 0 : flags |= NH_FLAG_MCAST;
936 : }
937 :
938 0 : if (opt_set(CFA_OPT_IND))
939 0 : flags |= NH_FLAG_COMPOSITE_FABRIC;
940 :
941 0 : if (opt_set(CEN_OPT_IND))
942 0 : flags |= NH_FLAG_COMPOSITE_ENCAP;
943 :
944 0 : if (opt_set(CEVPN_OPT_IND)) {
945 0 : flags |= NH_FLAG_COMPOSITE_EVPN;
946 :
947 0 : if (!opt_set(LBL_OPT_IND))
948 0 : cmd_usage();
949 : }
950 :
951 0 : if (opt_set(TOR_OPT_IND)) {
952 0 : flags |= NH_FLAG_COMPOSITE_TOR;
953 :
954 0 : if (!opt_set(LBL_OPT_IND))
955 0 : cmd_usage();
956 : }
957 :
958 0 : if (opt_set(ECMP_OPT_IND)) {
959 0 : flags |= NH_FLAG_COMPOSITE_ECMP;
960 : }
961 :
962 0 : if (memcmp(opt, zero_opt, sizeof(opt)))
963 0 : cmd_usage();
964 :
965 0 : } else if (type == NH_VRF_TRANSLATE) {
966 0 : if (opt_set(UUCF_OPT_IND))
967 0 : flags |= NH_FLAG_UNKNOWN_UC_FLOOD;
968 : } else {
969 0 : cmd_usage();
970 : }
971 :
972 0 : break;
973 :
974 0 : case SANDESH_OP_DEL:
975 0 : if (!nh_set)
976 0 : cmd_usage();
977 :
978 0 : if (memcmp(opt, zero_opt, sizeof(opt)))
979 0 : cmd_usage();
980 0 : break;
981 :
982 4 : case SANDESH_OP_DUMP:
983 : case SANDESH_OP_GET:
984 4 : if (memcmp(opt, zero_opt, sizeof(opt)))
985 0 : usage();
986 4 : break;
987 : }
988 :
989 4 : return;
990 : }
991 :
992 :
993 : int
994 4 : main(int argc, char *argv[])
995 : {
996 : int opt, ind;
997 :
998 4 : nh_fill_nl_callbacks();
999 :
1000 12 : while ((opt = getopt_long(argc, argv, "",
1001 12 : long_options, &ind)) >= 0) {
1002 8 : switch (opt) {
1003 8 : case 0:
1004 8 : parse_long_opts(ind, optarg);
1005 8 : break;
1006 :
1007 0 : default:
1008 0 : usage();
1009 : }
1010 : }
1011 :
1012 4 : if (opt_set(SOCK_DIR_OPT_IND)) {
1013 4 : set_platform_vtest();
1014 : }
1015 4 : validate_options();
1016 :
1017 4 : cl = vr_get_nl_client(VR_NETLINK_PROTO_DEFAULT);
1018 4 : if (!cl) {
1019 0 : exit(1);
1020 : }
1021 :
1022 4 : vr_nh_op(cl, command, type, nh_id, if_id, vrf_id, dst_mac,
1023 : src_mac, sip, dip, flags);
1024 :
1025 4 : return 0;
1026 : }
|