Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0-or-later
2 : /*
3 : * Interface functions.
4 : * Copyright (C) 1997, 98 Kunihiro Ishiguro
5 : */
6 :
7 : #include <zebra.h>
8 :
9 : #include "linklist.h"
10 : #include "vector.h"
11 : #include "lib_errors.h"
12 : #include "vty.h"
13 : #include "command.h"
14 : #include "vrf.h"
15 : #include "if.h"
16 : #include "sockunion.h"
17 : #include "prefix.h"
18 : #include "memory.h"
19 : #include "table.h"
20 : #include "buffer.h"
21 : #include "log.h"
22 : #include "northbound_cli.h"
23 : #include "admin_group.h"
24 : #include "lib/if_clippy.c"
25 :
26 12 : DEFINE_MTYPE_STATIC(LIB, IF, "Interface");
27 12 : DEFINE_MTYPE_STATIC(LIB, IFDESC, "Intf Desc");
28 12 : DEFINE_MTYPE_STATIC(LIB, CONNECTED, "Connected");
29 12 : DEFINE_MTYPE_STATIC(LIB, NBR_CONNECTED, "Neighbor Connected");
30 12 : DEFINE_MTYPE(LIB, CONNECTED_LABEL, "Connected interface label");
31 12 : DEFINE_MTYPE_STATIC(LIB, IF_LINK_PARAMS, "Informational Link Parameters");
32 :
33 : static void if_set_name(struct interface *ifp, const char *name);
34 : static struct interface *if_lookup_by_ifindex(ifindex_t ifindex,
35 : vrf_id_t vrf_id);
36 : static struct interface *if_lookup_by_index_all_vrf(ifindex_t ifindex);
37 : static int if_cmp_func(const struct interface *, const struct interface *);
38 : static int if_cmp_index_func(const struct interface *ifp1,
39 : const struct interface *ifp2);
40 322 : RB_GENERATE(if_name_head, interface, name_entry, if_cmp_func);
41 668 : RB_GENERATE(if_index_head, interface, index_entry, if_cmp_index_func);
42 :
43 : DEFINE_QOBJ_TYPE(interface);
44 :
45 32 : DEFINE_HOOK(if_add, (struct interface *ifp), (ifp));
46 32 : DEFINE_KOOH(if_del, (struct interface *ifp), (ifp));
47 :
48 48 : DEFINE_HOOK(if_real, (struct interface *ifp), (ifp));
49 0 : DEFINE_KOOH(if_unreal, (struct interface *ifp), (ifp));
50 :
51 24 : DEFINE_HOOK(if_up, (struct interface *ifp), (ifp));
52 12 : DEFINE_KOOH(if_down, (struct interface *ifp), (ifp));
53 :
54 : /* Compare interface names, returning an integer greater than, equal to, or
55 : * less than 0, (following the strcmp convention), according to the
56 : * relationship between ifp1 and ifp2. Interface names consist of an
57 : * alphabetic prefix and a numeric suffix. The primary sort key is
58 : * lexicographic by name, and then numeric by number. No number sorts
59 : * before all numbers. Examples: de0 < de1, de100 < fxp0 < xl0, devpty <
60 : * devpty0, de0 < del0
61 : */
62 177 : int if_cmp_name_func(const char *p1, const char *p2)
63 : {
64 177 : unsigned int l1, l2;
65 177 : long int x1, x2;
66 177 : int res;
67 :
68 354 : while (*p1 && *p2) {
69 290 : char *tmp1, *tmp2;
70 :
71 : /* look up to any number */
72 290 : l1 = strcspn(p1, "0123456789");
73 290 : l2 = strcspn(p2, "0123456789");
74 :
75 : /* name lengths are different -> compare names */
76 290 : if (l1 != l2)
77 113 : return (strcmp(p1, p2));
78 :
79 : /* Note that this relies on all numbers being less than all
80 : * letters, so
81 : * that de0 < del0.
82 : */
83 212 : res = strncmp(p1, p2, l1);
84 :
85 : /* names are different -> compare them */
86 212 : if (res)
87 0 : return res;
88 :
89 : /* with identical name part, go to numeric part */
90 212 : p1 += l1;
91 212 : p2 += l1;
92 :
93 212 : if (!*p1 && !*p2)
94 : return 0;
95 204 : if (!*p1)
96 : return -1;
97 204 : if (!*p2)
98 : return 1;
99 :
100 204 : x1 = strtol(p1, &tmp1, 10);
101 204 : x2 = strtol(p2, &tmp2, 10);
102 :
103 : /* let's compare numbers now */
104 204 : if (x1 < x2)
105 : return -1;
106 204 : if (x1 > x2)
107 : return 1;
108 :
109 : /* Compare string if numbers are equal (distinguish foo-1 from foo-001) */
110 177 : l1 = strspn(p1, "0123456789");
111 177 : l2 = strspn(p2, "0123456789");
112 177 : if (l1 != l2)
113 0 : return (strcmp(p1, p2));
114 :
115 : /* Continue to parse the rest of the string */
116 177 : p1 = (const char *)tmp1;
117 177 : p2 = (const char *)tmp2;
118 :
119 : /* numbers were equal, lets do it again..
120 : (it happens with name like "eth123.456:789") */
121 : }
122 64 : if (*p1)
123 : return 1;
124 64 : if (*p2)
125 16 : return -1;
126 : return 0;
127 : }
128 :
129 161 : static int if_cmp_func(const struct interface *ifp1,
130 : const struct interface *ifp2)
131 : {
132 161 : return if_cmp_name_func(ifp1->name, ifp2->name);
133 : }
134 :
135 334 : static int if_cmp_index_func(const struct interface *ifp1,
136 : const struct interface *ifp2)
137 : {
138 334 : if (ifp1->ifindex == ifp2->ifindex)
139 : return 0;
140 181 : else if (ifp1->ifindex > ifp2->ifindex)
141 : return 1;
142 : else
143 24 : return -1;
144 : }
145 :
146 36 : static void ifp_connected_free(void *arg)
147 : {
148 36 : struct connected *c = arg;
149 :
150 36 : connected_free(&c);
151 36 : }
152 :
153 : /* Create new interface structure. */
154 16 : static struct interface *if_new(struct vrf *vrf)
155 : {
156 16 : struct interface *ifp;
157 :
158 16 : assert(vrf);
159 :
160 16 : ifp = XCALLOC(MTYPE_IF, sizeof(struct interface));
161 :
162 16 : ifp->ifindex = IFINDEX_INTERNAL;
163 16 : ifp->name[0] = '\0';
164 :
165 16 : ifp->vrf = vrf;
166 :
167 16 : ifp->connected = list_new();
168 16 : ifp->connected->del = ifp_connected_free;
169 :
170 16 : ifp->nbr_connected = list_new();
171 16 : ifp->nbr_connected->del = (void (*)(void *))nbr_connected_free;
172 :
173 : /* Enable Link-detection by default */
174 16 : SET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION);
175 :
176 16 : QOBJ_REG(ifp, interface);
177 16 : return ifp;
178 : }
179 :
180 24 : void if_new_via_zapi(struct interface *ifp)
181 : {
182 24 : hook_call(if_real, ifp);
183 24 : }
184 :
185 0 : void if_destroy_via_zapi(struct interface *ifp)
186 : {
187 0 : hook_call(if_unreal, ifp);
188 :
189 0 : ifp->oldifindex = ifp->ifindex;
190 0 : if_set_index(ifp, IFINDEX_INTERNAL);
191 :
192 0 : if (!ifp->configured)
193 0 : if_delete(&ifp);
194 0 : }
195 :
196 12 : void if_up_via_zapi(struct interface *ifp)
197 : {
198 12 : hook_call(if_up, ifp);
199 12 : }
200 :
201 6 : void if_down_via_zapi(struct interface *ifp)
202 : {
203 6 : hook_call(if_down, ifp);
204 6 : }
205 :
206 16 : static struct interface *if_create_name(const char *name, struct vrf *vrf)
207 : {
208 16 : struct interface *ifp;
209 :
210 16 : ifp = if_new(vrf);
211 :
212 16 : if_set_name(ifp, name);
213 :
214 16 : hook_call(if_add, ifp);
215 16 : return ifp;
216 : }
217 :
218 : /* Create new interface structure. */
219 0 : void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id)
220 : {
221 0 : struct vrf *old_vrf, *vrf;
222 :
223 : /* remove interface from old master vrf list */
224 0 : old_vrf = ifp->vrf;
225 :
226 0 : if (ifp->name[0] != '\0')
227 0 : IFNAME_RB_REMOVE(old_vrf, ifp);
228 :
229 0 : if (ifp->ifindex != IFINDEX_INTERNAL)
230 0 : IFINDEX_RB_REMOVE(old_vrf, ifp);
231 :
232 0 : vrf = vrf_get(vrf_id, NULL);
233 0 : ifp->vrf = vrf;
234 :
235 0 : if (ifp->name[0] != '\0')
236 0 : IFNAME_RB_INSERT(vrf, ifp);
237 :
238 0 : if (ifp->ifindex != IFINDEX_INTERNAL)
239 0 : IFINDEX_RB_INSERT(vrf, ifp);
240 0 : }
241 :
242 :
243 : /* Delete interface structure. */
244 16 : void if_delete_retain(struct interface *ifp)
245 : {
246 16 : hook_call(if_del, ifp);
247 16 : QOBJ_UNREG(ifp);
248 :
249 : /* Free connected address list */
250 16 : list_delete_all_node(ifp->connected);
251 :
252 : /* Free connected nbr address list */
253 16 : list_delete_all_node(ifp->nbr_connected);
254 16 : }
255 :
256 : /* Delete and free interface structure. */
257 16 : void if_delete(struct interface **ifp)
258 : {
259 16 : struct interface *ptr = *ifp;
260 16 : struct vrf *vrf = ptr->vrf;
261 :
262 16 : IFNAME_RB_REMOVE(vrf, ptr);
263 16 : if (ptr->ifindex != IFINDEX_INTERNAL)
264 16 : IFINDEX_RB_REMOVE(vrf, ptr);
265 :
266 16 : if_delete_retain(ptr);
267 :
268 16 : list_delete(&ptr->connected);
269 16 : list_delete(&ptr->nbr_connected);
270 :
271 16 : if_link_params_free(ptr);
272 :
273 16 : XFREE(MTYPE_IFDESC, ptr->desc);
274 :
275 16 : XFREE(MTYPE_IF, ptr);
276 16 : *ifp = NULL;
277 16 : }
278 :
279 : /* Used only internally to check within VRF only */
280 169 : static struct interface *if_lookup_by_ifindex(ifindex_t ifindex,
281 : vrf_id_t vrf_id)
282 : {
283 169 : struct vrf *vrf;
284 169 : struct interface if_tmp;
285 :
286 169 : vrf = vrf_lookup_by_id(vrf_id);
287 169 : if (!vrf)
288 : return NULL;
289 :
290 169 : if_tmp.ifindex = ifindex;
291 169 : return RB_FIND(if_index_head, &vrf->ifaces_by_index, &if_tmp);
292 : }
293 :
294 : /* Interface existence check by index. */
295 153 : struct interface *if_lookup_by_index(ifindex_t ifindex, vrf_id_t vrf_id)
296 : {
297 153 : switch (vrf_get_backend()) {
298 0 : case VRF_BACKEND_UNKNOWN:
299 : case VRF_BACKEND_NETNS:
300 0 : return(if_lookup_by_ifindex(ifindex, vrf_id));
301 153 : case VRF_BACKEND_VRF_LITE:
302 153 : return(if_lookup_by_index_all_vrf(ifindex));
303 : }
304 : return NULL;
305 : }
306 :
307 : /* Interface existence check by index. */
308 0 : struct interface *if_vrf_lookup_by_index_next(ifindex_t ifindex,
309 : vrf_id_t vrf_id)
310 : {
311 0 : struct vrf *vrf = vrf_lookup_by_id(vrf_id);
312 0 : struct interface *tmp_ifp;
313 0 : bool found = false;
314 :
315 0 : if (!vrf)
316 : return NULL;
317 :
318 0 : if (ifindex == 0) {
319 0 : tmp_ifp = RB_MIN(if_index_head, &vrf->ifaces_by_index);
320 : /* skip the vrf interface */
321 0 : if (tmp_ifp && if_is_vrf(tmp_ifp))
322 0 : ifindex = tmp_ifp->ifindex;
323 : else
324 0 : return tmp_ifp;
325 : }
326 :
327 0 : RB_FOREACH (tmp_ifp, if_index_head, &vrf->ifaces_by_index) {
328 0 : if (found) {
329 : /* skip the vrf interface */
330 0 : if (tmp_ifp && if_is_vrf(tmp_ifp))
331 0 : continue;
332 : else
333 0 : return tmp_ifp;
334 : }
335 0 : if (tmp_ifp->ifindex == ifindex)
336 0 : found = true;
337 : }
338 : return NULL;
339 : }
340 :
341 0 : const char *ifindex2ifname(ifindex_t ifindex, vrf_id_t vrf_id)
342 : {
343 0 : struct interface *ifp;
344 :
345 0 : return ((ifp = if_lookup_by_index(ifindex, vrf_id)) != NULL)
346 : ? ifp->name
347 0 : : "unknown";
348 : }
349 :
350 2 : ifindex_t ifname2ifindex(const char *name, vrf_id_t vrf_id)
351 : {
352 2 : struct interface *ifp;
353 :
354 2 : return ((ifp = if_lookup_by_name(name, vrf_id)) != NULL)
355 : ? ifp->ifindex
356 2 : : IFINDEX_INTERNAL;
357 : }
358 :
359 : /* Interface existence check by interface name. */
360 40 : struct interface *if_lookup_by_name(const char *name, vrf_id_t vrf_id)
361 : {
362 40 : struct vrf *vrf = vrf_lookup_by_id(vrf_id);
363 40 : struct interface if_tmp;
364 :
365 40 : if (!vrf || !name
366 40 : || strnlen(name, INTERFACE_NAMSIZ) == INTERFACE_NAMSIZ)
367 : return NULL;
368 :
369 40 : strlcpy(if_tmp.name, name, sizeof(if_tmp.name));
370 40 : return RB_FIND(if_name_head, &vrf->ifaces_by_name, &if_tmp);
371 : }
372 :
373 32 : struct interface *if_lookup_by_name_vrf(const char *name, struct vrf *vrf)
374 : {
375 32 : struct interface if_tmp;
376 :
377 32 : if (!name || strnlen(name, INTERFACE_NAMSIZ) == INTERFACE_NAMSIZ)
378 : return NULL;
379 :
380 32 : strlcpy(if_tmp.name, name, sizeof(if_tmp.name));
381 32 : return RB_FIND(if_name_head, &vrf->ifaces_by_name, &if_tmp);
382 : }
383 :
384 32 : static struct interface *if_lookup_by_name_all_vrf(const char *name)
385 : {
386 32 : struct vrf *vrf;
387 32 : struct interface *ifp;
388 :
389 32 : if (!name || strnlen(name, INTERFACE_NAMSIZ) == INTERFACE_NAMSIZ)
390 : return NULL;
391 :
392 80 : RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
393 32 : ifp = if_lookup_by_name_vrf(name, vrf);
394 32 : if (ifp)
395 16 : return ifp;
396 : }
397 :
398 : return NULL;
399 : }
400 :
401 153 : static struct interface *if_lookup_by_index_all_vrf(ifindex_t ifindex)
402 : {
403 153 : struct vrf *vrf;
404 153 : struct interface *ifp;
405 :
406 153 : if (ifindex == IFINDEX_INTERNAL)
407 : return NULL;
408 :
409 306 : RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
410 153 : ifp = if_lookup_by_ifindex(ifindex, vrf->vrf_id);
411 153 : if (ifp)
412 153 : return ifp;
413 : }
414 :
415 : return NULL;
416 : }
417 :
418 : /* Lookup interface by IP address.
419 : *
420 : * supersedes if_lookup_exact_address(), which didn't care about up/down
421 : * state. but all users we have either only care if the address is local
422 : * (=> use if_address_is_local() please), or care about UP interfaces before
423 : * anything else
424 : *
425 : * to accept only UP interfaces, check if_is_up() on the returned ifp.
426 : */
427 0 : struct interface *if_lookup_address_local(const void *src, int family,
428 : vrf_id_t vrf_id)
429 : {
430 0 : struct vrf *vrf = vrf_lookup_by_id(vrf_id);
431 0 : struct listnode *cnode;
432 0 : struct interface *ifp, *best_down = NULL;
433 0 : struct prefix *p;
434 0 : struct connected *c;
435 :
436 0 : if (family != AF_INET && family != AF_INET6)
437 : return NULL;
438 :
439 0 : FOR_ALL_INTERFACES (vrf, ifp) {
440 0 : for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) {
441 0 : p = c->address;
442 :
443 0 : if (!p || p->family != family)
444 0 : continue;
445 :
446 0 : if (family == AF_INET) {
447 0 : if (!IPV4_ADDR_SAME(&p->u.prefix4,
448 : (struct in_addr *)src))
449 0 : continue;
450 0 : } else if (family == AF_INET6) {
451 0 : if (!IPV6_ADDR_SAME(&p->u.prefix6,
452 : (struct in6_addr *)src))
453 0 : continue;
454 : }
455 :
456 0 : if (if_is_up(ifp))
457 0 : return ifp;
458 0 : if (!best_down)
459 0 : best_down = ifp;
460 : }
461 : }
462 : return best_down;
463 : }
464 :
465 : /* Lookup interface by IP address. */
466 0 : struct connected *if_lookup_address(const void *matchaddr, int family,
467 : vrf_id_t vrf_id)
468 : {
469 0 : struct vrf *vrf = vrf_lookup_by_id(vrf_id);
470 0 : struct prefix addr;
471 0 : int bestlen = 0;
472 0 : struct listnode *cnode;
473 0 : struct interface *ifp;
474 0 : struct connected *c;
475 0 : struct connected *match;
476 :
477 0 : if (family == AF_INET) {
478 0 : addr.family = AF_INET;
479 0 : addr.u.prefix4 = *((struct in_addr *)matchaddr);
480 0 : addr.prefixlen = IPV4_MAX_BITLEN;
481 0 : } else if (family == AF_INET6) {
482 0 : addr.family = AF_INET6;
483 0 : addr.u.prefix6 = *((struct in6_addr *)matchaddr);
484 0 : addr.prefixlen = IPV6_MAX_BITLEN;
485 : } else
486 0 : assert(!"Attempted lookup of family not supported");
487 :
488 0 : match = NULL;
489 :
490 0 : FOR_ALL_INTERFACES (vrf, ifp) {
491 0 : for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) {
492 0 : if (c->address && (c->address->family == AF_INET)
493 0 : && prefix_match(CONNECTED_PREFIX(c), &addr)
494 0 : && (c->address->prefixlen > bestlen)) {
495 0 : bestlen = c->address->prefixlen;
496 0 : match = c;
497 : }
498 : }
499 : }
500 0 : return match;
501 : }
502 :
503 : /* Lookup interface by prefix */
504 0 : struct interface *if_lookup_prefix(const struct prefix *prefix, vrf_id_t vrf_id)
505 : {
506 0 : struct vrf *vrf = vrf_lookup_by_id(vrf_id);
507 0 : struct listnode *cnode;
508 0 : struct interface *ifp;
509 0 : struct connected *c;
510 :
511 0 : FOR_ALL_INTERFACES (vrf, ifp) {
512 0 : for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) {
513 0 : if (prefix_cmp(c->address, prefix) == 0) {
514 0 : return ifp;
515 : }
516 : }
517 : }
518 : return NULL;
519 : }
520 :
521 0 : size_t if_lookup_by_hwaddr(const uint8_t *hw_addr, size_t addrsz,
522 : struct interface ***result, vrf_id_t vrf_id)
523 : {
524 0 : struct vrf *vrf = vrf_lookup_by_id(vrf_id);
525 :
526 0 : struct list *rs = list_new();
527 0 : struct interface *ifp;
528 :
529 0 : FOR_ALL_INTERFACES (vrf, ifp) {
530 0 : if (ifp->hw_addr_len == (int)addrsz
531 0 : && !memcmp(hw_addr, ifp->hw_addr, addrsz))
532 0 : listnode_add(rs, ifp);
533 : }
534 :
535 0 : if (rs->count) {
536 0 : *result = XCALLOC(MTYPE_TMP,
537 : sizeof(struct interface *) * rs->count);
538 0 : list_to_array(rs, (void **)*result, rs->count);
539 : }
540 :
541 0 : int count = rs->count;
542 :
543 0 : list_delete(&rs);
544 :
545 0 : return count;
546 : }
547 :
548 : /* Get the VRF loopback interface, i.e. the loopback on the default VRF
549 : * or the VRF interface.
550 : */
551 0 : struct interface *if_get_vrf_loopback(vrf_id_t vrf_id)
552 : {
553 0 : struct interface *ifp = NULL;
554 0 : struct vrf *vrf = vrf_lookup_by_id(vrf_id);
555 :
556 0 : FOR_ALL_INTERFACES (vrf, ifp)
557 0 : if (if_is_loopback(ifp))
558 0 : return ifp;
559 :
560 : return NULL;
561 : }
562 :
563 : /* Get interface by name if given name interface doesn't exist create
564 : one. */
565 32 : struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id,
566 : const char *vrf_name)
567 : {
568 32 : struct interface *ifp = NULL;
569 32 : struct vrf *vrf;
570 :
571 32 : switch (vrf_get_backend()) {
572 0 : case VRF_BACKEND_UNKNOWN:
573 : case VRF_BACKEND_NETNS:
574 0 : vrf = vrf_get(vrf_id, vrf_name);
575 0 : assert(vrf);
576 :
577 0 : ifp = if_lookup_by_name_vrf(name, vrf);
578 0 : if (ifp) {
579 : /* If it came from the kernel or by way of zclient,
580 : * believe it and update the ifp accordingly.
581 : */
582 0 : if (ifp->vrf->vrf_id != vrf_id && vrf_id != VRF_UNKNOWN)
583 0 : if_update_to_new_vrf(ifp, vrf_id);
584 :
585 0 : return ifp;
586 : }
587 :
588 : break;
589 32 : case VRF_BACKEND_VRF_LITE:
590 32 : ifp = if_lookup_by_name_all_vrf(name);
591 32 : if (ifp) {
592 : /* If it came from the kernel or by way of zclient,
593 : * believe it and update the ifp accordingly.
594 : */
595 16 : if (ifp->vrf->vrf_id != vrf_id && vrf_id != VRF_UNKNOWN)
596 0 : if_update_to_new_vrf(ifp, vrf_id);
597 :
598 16 : return ifp;
599 : }
600 :
601 16 : vrf = vrf_get(vrf_id, vrf_name);
602 16 : assert(vrf);
603 :
604 : break;
605 : default:
606 : return NULL;
607 : }
608 :
609 16 : return if_create_name(name, vrf);
610 : }
611 :
612 59 : int if_set_index(struct interface *ifp, ifindex_t ifindex)
613 : {
614 59 : if (ifp->ifindex == ifindex)
615 : return 0;
616 :
617 : /*
618 : * If there is already an interface with this ifindex, we will collide
619 : * on insertion, so don't even try.
620 : */
621 16 : if (if_lookup_by_ifindex(ifindex, ifp->vrf->vrf_id))
622 : return -1;
623 :
624 16 : if (ifp->ifindex != IFINDEX_INTERNAL)
625 0 : IFINDEX_RB_REMOVE(ifp->vrf, ifp);
626 :
627 16 : ifp->ifindex = ifindex;
628 :
629 16 : if (ifp->ifindex != IFINDEX_INTERNAL) {
630 : /*
631 : * This should never happen, since we checked if there was
632 : * already an interface with the desired ifindex at the top of
633 : * the function. Nevertheless.
634 : */
635 16 : if (IFINDEX_RB_INSERT(ifp->vrf, ifp))
636 0 : return -1;
637 : }
638 :
639 : return 0;
640 : }
641 :
642 16 : static void if_set_name(struct interface *ifp, const char *name)
643 : {
644 16 : if (if_cmp_name_func(ifp->name, name) == 0)
645 : return;
646 :
647 16 : if (ifp->name[0] != '\0')
648 0 : IFNAME_RB_REMOVE(ifp->vrf, ifp);
649 :
650 16 : strlcpy(ifp->name, name, sizeof(ifp->name));
651 :
652 16 : if (ifp->name[0] != '\0')
653 16 : IFNAME_RB_INSERT(ifp->vrf, ifp);
654 : }
655 :
656 : /* Does interface up ? */
657 0 : int if_is_up(const struct interface *ifp)
658 : {
659 0 : return ifp->flags & IFF_UP;
660 : }
661 :
662 : /* Is interface running? */
663 0 : int if_is_running(const struct interface *ifp)
664 : {
665 0 : return ifp->flags & IFF_RUNNING;
666 : }
667 :
668 : /* Is the interface operative, eg. either UP & RUNNING
669 : or UP & !ZEBRA_INTERFACE_LINK_DETECTION and
670 : if ptm checking is enabled, then ptm check has passed */
671 167 : int if_is_operative(const struct interface *ifp)
672 : {
673 167 : return ((ifp->flags & IFF_UP)
674 167 : && (((ifp->flags & IFF_RUNNING)
675 159 : && (ifp->ptm_status || !ifp->ptm_enable))
676 8 : || !CHECK_FLAG(ifp->status,
677 : ZEBRA_INTERFACE_LINKDETECTION)));
678 : }
679 :
680 : /* Is the interface operative, eg. either UP & RUNNING
681 : or UP & !ZEBRA_INTERFACE_LINK_DETECTION, without PTM check */
682 14 : int if_is_no_ptm_operative(const struct interface *ifp)
683 : {
684 14 : return ((ifp->flags & IFF_UP)
685 14 : && ((ifp->flags & IFF_RUNNING)
686 7 : || !CHECK_FLAG(ifp->status,
687 : ZEBRA_INTERFACE_LINKDETECTION)));
688 : }
689 :
690 : /* Is this loopback interface ? */
691 107 : int if_is_loopback_exact(const struct interface *ifp)
692 : {
693 : /* XXX: Do this better, eg what if IFF_WHATEVER means X on platform M
694 : * but Y on platform N?
695 : */
696 107 : return (ifp->flags & (IFF_LOOPBACK | IFF_NOXMIT | IFF_VIRTUAL));
697 : }
698 :
699 : /* Check interface is VRF */
700 88 : int if_is_vrf(const struct interface *ifp)
701 : {
702 88 : return CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK);
703 : }
704 :
705 : /* Should this interface be treated as a loopback? */
706 107 : bool if_is_loopback(const struct interface *ifp)
707 : {
708 107 : if (if_is_loopback_exact(ifp) || if_is_vrf(ifp))
709 19 : return true;
710 :
711 : return false;
712 : }
713 :
714 : /* Does this interface support broadcast ? */
715 0 : int if_is_broadcast(const struct interface *ifp)
716 : {
717 0 : return ifp->flags & IFF_BROADCAST;
718 : }
719 :
720 : /* Does this interface support pointopoint ? */
721 2 : int if_is_pointopoint(const struct interface *ifp)
722 : {
723 2 : return ifp->flags & IFF_POINTOPOINT;
724 : }
725 :
726 : /* Does this interface support multicast ? */
727 0 : int if_is_multicast(const struct interface *ifp)
728 : {
729 0 : return ifp->flags & IFF_MULTICAST;
730 : }
731 :
732 : /* Printout flag information into log */
733 0 : const char *if_flag_dump(unsigned long flag)
734 : {
735 0 : int separator = 0;
736 0 : static char logbuf[BUFSIZ];
737 :
738 : #define IFF_OUT_LOG(X, STR) \
739 : if (flag & (X)) { \
740 : if (separator) \
741 : strlcat(logbuf, ",", sizeof(logbuf)); \
742 : else \
743 : separator = 1; \
744 : strlcat(logbuf, STR, sizeof(logbuf)); \
745 : }
746 :
747 0 : strlcpy(logbuf, "<", BUFSIZ);
748 0 : IFF_OUT_LOG(IFF_UP, "UP");
749 0 : IFF_OUT_LOG(IFF_BROADCAST, "BROADCAST");
750 0 : IFF_OUT_LOG(IFF_DEBUG, "DEBUG");
751 0 : IFF_OUT_LOG(IFF_LOOPBACK, "LOOPBACK");
752 0 : IFF_OUT_LOG(IFF_POINTOPOINT, "POINTOPOINT");
753 0 : IFF_OUT_LOG(IFF_NOTRAILERS, "NOTRAILERS");
754 0 : IFF_OUT_LOG(IFF_RUNNING, "RUNNING");
755 0 : IFF_OUT_LOG(IFF_NOARP, "NOARP");
756 0 : IFF_OUT_LOG(IFF_PROMISC, "PROMISC");
757 0 : IFF_OUT_LOG(IFF_ALLMULTI, "ALLMULTI");
758 0 : IFF_OUT_LOG(IFF_OACTIVE, "OACTIVE");
759 0 : IFF_OUT_LOG(IFF_SIMPLEX, "SIMPLEX");
760 0 : IFF_OUT_LOG(IFF_LINK0, "LINK0");
761 0 : IFF_OUT_LOG(IFF_LINK1, "LINK1");
762 0 : IFF_OUT_LOG(IFF_LINK2, "LINK2");
763 0 : IFF_OUT_LOG(IFF_MULTICAST, "MULTICAST");
764 0 : IFF_OUT_LOG(IFF_NOXMIT, "NOXMIT");
765 0 : IFF_OUT_LOG(IFF_NORTEXCH, "NORTEXCH");
766 0 : IFF_OUT_LOG(IFF_VIRTUAL, "VIRTUAL");
767 0 : IFF_OUT_LOG(IFF_IPV4, "IPv4");
768 0 : IFF_OUT_LOG(IFF_IPV6, "IPv6");
769 :
770 0 : strlcat(logbuf, ">", sizeof(logbuf));
771 :
772 0 : return logbuf;
773 : #undef IFF_OUT_LOG
774 : }
775 :
776 : /* For debugging */
777 0 : static void if_dump(const struct interface *ifp)
778 : {
779 0 : struct listnode *node;
780 0 : struct connected *c __attribute__((unused));
781 :
782 0 : for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, c))
783 0 : zlog_info(
784 : "Interface %s vrf %s(%u) index %d metric %d mtu %d mtu6 %d %s",
785 : ifp->name, ifp->vrf->name, ifp->vrf->vrf_id,
786 : ifp->ifindex, ifp->metric, ifp->mtu, ifp->mtu6,
787 : if_flag_dump(ifp->flags));
788 0 : }
789 :
790 : /* Interface printing for all interface. */
791 0 : void if_dump_all(void)
792 : {
793 0 : struct vrf *vrf;
794 0 : void *ifp;
795 :
796 0 : RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id)
797 0 : FOR_ALL_INTERFACES (vrf, ifp)
798 0 : if_dump(ifp);
799 0 : }
800 :
801 : /* Allocate connected structure. */
802 40 : struct connected *connected_new(void)
803 : {
804 40 : return XCALLOC(MTYPE_CONNECTED, sizeof(struct connected));
805 : }
806 :
807 : /* Allocate nbr connected structure. */
808 0 : struct nbr_connected *nbr_connected_new(void)
809 : {
810 0 : return XCALLOC(MTYPE_NBR_CONNECTED, sizeof(struct nbr_connected));
811 : }
812 :
813 : /* Free connected structure. */
814 40 : void connected_free(struct connected **connected)
815 : {
816 40 : struct connected *ptr = *connected;
817 :
818 40 : prefix_free(&ptr->address);
819 40 : prefix_free(&ptr->destination);
820 :
821 40 : XFREE(MTYPE_CONNECTED_LABEL, ptr->label);
822 :
823 40 : XFREE(MTYPE_CONNECTED, ptr);
824 40 : *connected = NULL;
825 40 : }
826 :
827 : /* Free nbr connected structure. */
828 0 : void nbr_connected_free(struct nbr_connected *connected)
829 : {
830 0 : if (connected->address)
831 0 : prefix_free(&connected->address);
832 :
833 0 : XFREE(MTYPE_NBR_CONNECTED, connected);
834 0 : }
835 :
836 : /* If same interface nbr address already exists... */
837 0 : struct nbr_connected *nbr_connected_check(struct interface *ifp,
838 : struct prefix *p)
839 : {
840 0 : struct nbr_connected *ifc;
841 0 : struct listnode *node;
842 :
843 0 : for (ALL_LIST_ELEMENTS_RO(ifp->nbr_connected, node, ifc))
844 0 : if (prefix_same(ifc->address, p))
845 0 : return ifc;
846 :
847 : return NULL;
848 : }
849 :
850 : /* Print if_addr structure. */
851 : static void __attribute__((unused))
852 : connected_log(struct connected *connected, char *str)
853 : {
854 : struct prefix *p;
855 : struct interface *ifp;
856 : char logbuf[BUFSIZ];
857 : char buf[BUFSIZ];
858 :
859 : ifp = connected->ifp;
860 : p = connected->address;
861 :
862 : snprintf(logbuf, sizeof(logbuf), "%s interface %s vrf %s(%u) %s %pFX ",
863 : str, ifp->name, ifp->vrf->name, ifp->vrf->vrf_id,
864 : prefix_family_str(p), p);
865 :
866 : p = connected->destination;
867 : if (p) {
868 : strlcat(logbuf, inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ),
869 : BUFSIZ);
870 : }
871 : zlog_info("%s", logbuf);
872 : }
873 :
874 : /* Print if_addr structure. */
875 : static void __attribute__((unused))
876 : nbr_connected_log(struct nbr_connected *connected, char *str)
877 : {
878 : struct prefix *p;
879 : struct interface *ifp;
880 : char logbuf[BUFSIZ];
881 :
882 : ifp = connected->ifp;
883 : p = connected->address;
884 :
885 : snprintf(logbuf, sizeof(logbuf), "%s interface %s %s %pFX ", str,
886 : ifp->name, prefix_family_str(p), p);
887 :
888 : zlog_info("%s", logbuf);
889 : }
890 :
891 : /* If two connected address has same prefix return 1. */
892 34 : static int connected_same_prefix(const struct prefix *p1,
893 : const struct prefix *p2)
894 : {
895 34 : if (p1->family == p2->family) {
896 23 : if (p1->family == AF_INET
897 5 : && IPV4_ADDR_SAME(&p1->u.prefix4, &p2->u.prefix4))
898 : return 1;
899 18 : if (p1->family == AF_INET6
900 18 : && IPV6_ADDR_SAME(&p1->u.prefix6, &p2->u.prefix6))
901 : return 1;
902 : }
903 : return 0;
904 : }
905 :
906 : /* count the number of connected addresses that are in the given family */
907 0 : unsigned int connected_count_by_family(struct interface *ifp, int family)
908 : {
909 0 : struct listnode *cnode;
910 0 : struct connected *connected;
911 0 : unsigned int cnt = 0;
912 :
913 0 : for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected))
914 0 : if (connected->address->family == family)
915 0 : cnt++;
916 :
917 0 : return cnt;
918 : }
919 :
920 35 : struct connected *connected_lookup_prefix_exact(struct interface *ifp,
921 : const struct prefix *p)
922 : {
923 35 : struct listnode *node;
924 35 : struct listnode *next;
925 35 : struct connected *ifc;
926 :
927 87 : for (node = listhead(ifp->connected); node; node = next) {
928 30 : ifc = listgetdata(node);
929 30 : next = node->next;
930 :
931 30 : if (connected_same_prefix(ifc->address, p))
932 13 : return ifc;
933 : }
934 : return NULL;
935 : }
936 :
937 4 : struct connected *connected_delete_by_prefix(struct interface *ifp,
938 : struct prefix *p)
939 : {
940 4 : struct listnode *node;
941 4 : struct listnode *next;
942 4 : struct connected *ifc;
943 :
944 : /* In case of same prefix come, replace it with new one. */
945 8 : for (node = listhead(ifp->connected); node; node = next) {
946 4 : ifc = listgetdata(node);
947 4 : next = node->next;
948 :
949 4 : if (connected_same_prefix(ifc->address, p)) {
950 4 : listnode_delete(ifp->connected, ifc);
951 4 : return ifc;
952 : }
953 : }
954 : return NULL;
955 : }
956 :
957 : /* Find the address on our side that will be used when packets
958 : are sent to dst. */
959 0 : struct connected *connected_lookup_prefix(struct interface *ifp,
960 : const struct prefix *addr)
961 : {
962 0 : struct listnode *cnode;
963 0 : struct connected *c;
964 0 : struct connected *match;
965 :
966 0 : match = NULL;
967 :
968 0 : for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) {
969 0 : if (c->address && (c->address->family == addr->family)
970 0 : && prefix_match(CONNECTED_PREFIX(c), addr)
971 0 : && (!match
972 0 : || (c->address->prefixlen > match->address->prefixlen)))
973 0 : match = c;
974 : }
975 0 : return match;
976 : }
977 :
978 22 : struct connected *connected_add_by_prefix(struct interface *ifp,
979 : struct prefix *p,
980 : struct prefix *destination)
981 : {
982 22 : struct connected *ifc;
983 :
984 : /* Allocate new connected address. */
985 22 : ifc = connected_new();
986 22 : ifc->ifp = ifp;
987 :
988 : /* Fetch interface address */
989 22 : ifc->address = prefix_new();
990 22 : memcpy(ifc->address, p, sizeof(struct prefix));
991 :
992 : /* Fetch dest address */
993 22 : if (destination) {
994 0 : ifc->destination = prefix_new();
995 0 : memcpy(ifc->destination, destination, sizeof(struct prefix));
996 : }
997 :
998 : /* Add connected address to the interface. */
999 22 : listnode_add(ifp->connected, ifc);
1000 22 : return ifc;
1001 : }
1002 :
1003 33 : struct connected *connected_get_linklocal(struct interface *ifp)
1004 : {
1005 33 : struct listnode *n;
1006 33 : struct connected *c = NULL;
1007 :
1008 110 : for (ALL_LIST_ELEMENTS_RO(ifp->connected, n, c)) {
1009 77 : if (c->address->family == AF_INET6
1010 66 : && IN6_IS_ADDR_LINKLOCAL(&c->address->u.prefix6))
1011 : break;
1012 : }
1013 33 : return c;
1014 : }
1015 :
1016 4 : void if_terminate(struct vrf *vrf)
1017 : {
1018 4 : struct interface *ifp;
1019 :
1020 20 : while (!RB_EMPTY(if_name_head, &vrf->ifaces_by_name)) {
1021 16 : ifp = RB_ROOT(if_name_head, &vrf->ifaces_by_name);
1022 :
1023 16 : if (ifp->node) {
1024 8 : ifp->node->info = NULL;
1025 8 : route_unlock_node(ifp->node);
1026 8 : ifp->node = NULL;
1027 : }
1028 16 : if_delete(&ifp);
1029 : }
1030 4 : }
1031 :
1032 0 : const char *if_link_type_str(enum zebra_link_type llt)
1033 : {
1034 0 : switch (llt) {
1035 : #define llts(T,S) case (T): return (S)
1036 : llts(ZEBRA_LLT_UNKNOWN, "Unknown");
1037 0 : llts(ZEBRA_LLT_ETHER, "Ethernet");
1038 0 : llts(ZEBRA_LLT_EETHER, "Experimental Ethernet");
1039 0 : llts(ZEBRA_LLT_AX25, "AX.25 Level 2");
1040 0 : llts(ZEBRA_LLT_PRONET, "PROnet token ring");
1041 0 : llts(ZEBRA_LLT_IEEE802, "IEEE 802.2 Ethernet/TR/TB");
1042 0 : llts(ZEBRA_LLT_ARCNET, "ARCnet");
1043 0 : llts(ZEBRA_LLT_APPLETLK, "AppleTalk");
1044 0 : llts(ZEBRA_LLT_DLCI, "Frame Relay DLCI");
1045 0 : llts(ZEBRA_LLT_ATM, "ATM");
1046 0 : llts(ZEBRA_LLT_METRICOM, "Metricom STRIP");
1047 0 : llts(ZEBRA_LLT_IEEE1394, "IEEE 1394 IPv4");
1048 0 : llts(ZEBRA_LLT_EUI64, "EUI-64");
1049 0 : llts(ZEBRA_LLT_INFINIBAND, "InfiniBand");
1050 0 : llts(ZEBRA_LLT_SLIP, "SLIP");
1051 0 : llts(ZEBRA_LLT_CSLIP, "Compressed SLIP");
1052 0 : llts(ZEBRA_LLT_SLIP6, "SLIPv6");
1053 0 : llts(ZEBRA_LLT_CSLIP6, "Compressed SLIPv6");
1054 0 : llts(ZEBRA_LLT_RSRVD, "Reserved");
1055 0 : llts(ZEBRA_LLT_ADAPT, "Adapt");
1056 0 : llts(ZEBRA_LLT_ROSE, "ROSE packet radio");
1057 0 : llts(ZEBRA_LLT_X25, "CCITT X.25");
1058 0 : llts(ZEBRA_LLT_PPP, "PPP");
1059 0 : llts(ZEBRA_LLT_CHDLC, "Cisco HDLC");
1060 0 : llts(ZEBRA_LLT_RAWHDLC, "Raw HDLC");
1061 0 : llts(ZEBRA_LLT_LAPB, "LAPB");
1062 0 : llts(ZEBRA_LLT_IPIP, "IPIP Tunnel");
1063 0 : llts(ZEBRA_LLT_IPIP6, "IPIP6 Tunnel");
1064 0 : llts(ZEBRA_LLT_FRAD, "FRAD");
1065 0 : llts(ZEBRA_LLT_SKIP, "SKIP vif");
1066 0 : llts(ZEBRA_LLT_LOOPBACK, "Loopback");
1067 0 : llts(ZEBRA_LLT_LOCALTLK, "Localtalk");
1068 0 : llts(ZEBRA_LLT_FDDI, "FDDI");
1069 0 : llts(ZEBRA_LLT_SIT, "IPv6-in-IPv4 SIT");
1070 0 : llts(ZEBRA_LLT_IPDDP, "IP-in-DDP tunnel");
1071 0 : llts(ZEBRA_LLT_IPGRE, "GRE over IP");
1072 0 : llts(ZEBRA_LLT_IP6GRE, "GRE over IPv6");
1073 0 : llts(ZEBRA_LLT_PIMREG, "PIMSM registration");
1074 0 : llts(ZEBRA_LLT_HIPPI, "HiPPI");
1075 0 : llts(ZEBRA_LLT_ECONET, "Acorn Econet");
1076 0 : llts(ZEBRA_LLT_IRDA, "IrDA");
1077 0 : llts(ZEBRA_LLT_FCPP, "Fibre-Channel PtP");
1078 0 : llts(ZEBRA_LLT_FCAL, "Fibre-Channel Arbitrated Loop");
1079 0 : llts(ZEBRA_LLT_FCPL, "Fibre-Channel Public Loop");
1080 0 : llts(ZEBRA_LLT_FCFABRIC, "Fibre-Channel Fabric");
1081 0 : llts(ZEBRA_LLT_IEEE802_TR, "IEEE 802.2 Token Ring");
1082 0 : llts(ZEBRA_LLT_IEEE80211, "IEEE 802.11");
1083 0 : llts(ZEBRA_LLT_IEEE80211_RADIOTAP, "IEEE 802.11 Radiotap");
1084 0 : llts(ZEBRA_LLT_IEEE802154, "IEEE 802.15.4");
1085 0 : llts(ZEBRA_LLT_IEEE802154_PHY, "IEEE 802.15.4 Phy");
1086 : #undef llts
1087 : }
1088 0 : return NULL;
1089 : }
1090 :
1091 0 : bool if_link_params_cmp(struct if_link_params *iflp1,
1092 : struct if_link_params *iflp2)
1093 : {
1094 0 : struct if_link_params iflp1_copy, iflp2_copy;
1095 :
1096 : /* Extended admin-groups in if_link_params contain pointers.
1097 : * They cannot be compared with memcpy.
1098 : * Make copies of if_link_params without ext. admin-groups
1099 : * and compare separately the ext. admin-groups.
1100 : */
1101 0 : memcpy(&iflp1_copy, iflp1, sizeof(struct if_link_params));
1102 0 : memset(&iflp1_copy.ext_admin_grp, 0, sizeof(struct admin_group));
1103 :
1104 0 : memcpy(&iflp2_copy, iflp2, sizeof(struct if_link_params));
1105 0 : memset(&iflp2_copy.ext_admin_grp, 0, sizeof(struct admin_group));
1106 :
1107 0 : if (memcmp(&iflp1_copy, &iflp2_copy, sizeof(struct if_link_params)))
1108 : return false;
1109 :
1110 0 : if (!admin_group_cmp(&iflp1->ext_admin_grp, &iflp2->ext_admin_grp))
1111 : return false;
1112 :
1113 : return true;
1114 : }
1115 :
1116 0 : void if_link_params_copy(struct if_link_params *dst, struct if_link_params *src)
1117 : {
1118 0 : struct admin_group dst_ag;
1119 :
1120 : /* backup the admin_group structure that contains a pointer */
1121 0 : memcpy(&dst_ag, &dst->ext_admin_grp, sizeof(struct admin_group));
1122 : /* copy the if_link_params structure */
1123 0 : memcpy(dst, src, sizeof(struct if_link_params));
1124 : /* restore the admin_group structure */
1125 0 : memcpy(&dst->ext_admin_grp, &dst_ag, sizeof(struct admin_group));
1126 : /* copy src->ext_admin_grp data to dst->ext_admin_grp data memory */
1127 0 : admin_group_copy(&dst->ext_admin_grp, &src->ext_admin_grp);
1128 0 : }
1129 :
1130 0 : struct if_link_params *if_link_params_get(struct interface *ifp)
1131 : {
1132 0 : return ifp->link_params;
1133 : }
1134 :
1135 0 : struct if_link_params *if_link_params_enable(struct interface *ifp)
1136 : {
1137 0 : struct if_link_params *iflp;
1138 0 : int i;
1139 :
1140 0 : iflp = if_link_params_init(ifp);
1141 :
1142 : /* Compute default bandwidth based on interface */
1143 0 : iflp->default_bw =
1144 0 : ((ifp->bandwidth ? ifp->bandwidth : DEFAULT_BANDWIDTH)
1145 0 : * TE_MEGA_BIT / TE_BYTE);
1146 :
1147 : /* Set Max, Reservable and Unreserved Bandwidth */
1148 0 : iflp->max_bw = iflp->default_bw;
1149 0 : iflp->max_rsv_bw = iflp->default_bw;
1150 0 : for (i = 0; i < MAX_CLASS_TYPE; i++)
1151 0 : iflp->unrsv_bw[i] = iflp->default_bw;
1152 :
1153 : /* Update Link parameters status */
1154 0 : iflp->lp_status = LP_MAX_BW | LP_MAX_RSV_BW | LP_UNRSV_BW;
1155 :
1156 : /* Set TE metric equal to standard metric only if it is set */
1157 0 : if (ifp->metric != 0) {
1158 0 : iflp->te_metric = ifp->metric;
1159 0 : iflp->lp_status |= LP_TE_METRIC;
1160 : }
1161 :
1162 : /* Finally attach newly created Link Parameters */
1163 0 : ifp->link_params = iflp;
1164 :
1165 0 : return iflp;
1166 : }
1167 :
1168 0 : struct if_link_params *if_link_params_init(struct interface *ifp)
1169 : {
1170 0 : struct if_link_params *iflp = if_link_params_get(ifp);
1171 :
1172 0 : if (iflp)
1173 : return iflp;
1174 :
1175 0 : iflp = XCALLOC(MTYPE_IF_LINK_PARAMS, sizeof(struct if_link_params));
1176 :
1177 0 : admin_group_init(&iflp->ext_admin_grp);
1178 :
1179 0 : ifp->link_params = iflp;
1180 :
1181 0 : return iflp;
1182 : }
1183 :
1184 16 : void if_link_params_free(struct interface *ifp)
1185 : {
1186 16 : if (!ifp->link_params)
1187 : return;
1188 :
1189 0 : admin_group_term(&ifp->link_params->ext_admin_grp);
1190 0 : XFREE(MTYPE_IF_LINK_PARAMS, ifp->link_params);
1191 : }
1192 :
1193 : /* ----------- CLI commands ----------- */
1194 :
1195 : /* Guess the VRF of an interface. */
1196 0 : static int vrfname_by_ifname(const char *ifname, const char **vrfname)
1197 : {
1198 0 : struct vrf *vrf;
1199 0 : struct interface *ifp;
1200 0 : int count = 0;
1201 :
1202 0 : RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
1203 0 : FOR_ALL_INTERFACES (vrf, ifp) {
1204 0 : if (strmatch(ifp->name, ifname)) {
1205 0 : *vrfname = vrf->name;
1206 0 : count++;
1207 : }
1208 : }
1209 : }
1210 :
1211 0 : return count;
1212 : }
1213 :
1214 : /*
1215 : * XPath: /frr-interface:lib/interface
1216 : */
1217 0 : DEFPY_YANG_NOSH (interface,
1218 : interface_cmd,
1219 : "interface IFNAME [vrf NAME$vrf_name]",
1220 : "Select an interface to configure\n"
1221 : "Interface's name\n"
1222 : VRF_CMD_HELP_STR)
1223 : {
1224 0 : char xpath_list[XPATH_MAXLEN];
1225 0 : struct interface *ifp;
1226 0 : struct vrf *vrf;
1227 0 : int ret, count;
1228 :
1229 0 : if (vrf_is_backend_netns()) {
1230 : /*
1231 : * For backward compatibility, if the VRF name is not specified
1232 : * and there is exactly one interface with this name in the
1233 : * system, use its VRF. Otherwise fallback to the default VRF.
1234 : */
1235 0 : if (!vrf_name) {
1236 0 : count = vrfname_by_ifname(ifname, &vrf_name);
1237 0 : if (count != 1)
1238 0 : vrf_name = VRF_DEFAULT_NAME;
1239 : }
1240 :
1241 0 : snprintf(xpath_list, XPATH_MAXLEN,
1242 : "/frr-interface:lib/interface[name='%s:%s']", vrf_name,
1243 : ifname);
1244 : } else {
1245 0 : snprintf(xpath_list, XPATH_MAXLEN,
1246 : "/frr-interface:lib/interface[name='%s']", ifname);
1247 : }
1248 :
1249 0 : nb_cli_enqueue_change(vty, ".", NB_OP_CREATE, NULL);
1250 0 : ret = nb_cli_apply_changes_clear_pending(vty, "%s", xpath_list);
1251 0 : if (ret == CMD_SUCCESS) {
1252 0 : VTY_PUSH_XPATH(INTERFACE_NODE, xpath_list);
1253 :
1254 : /*
1255 : * For backward compatibility with old commands we still need
1256 : * to use the qobj infrastructure. This can be removed once
1257 : * all interface-level commands are converted to the new
1258 : * northbound model.
1259 : */
1260 0 : if (vrf_is_backend_netns()) {
1261 0 : vrf = vrf_lookup_by_name(vrf_name);
1262 0 : if (vrf)
1263 0 : ifp = if_lookup_by_name_vrf(ifname, vrf);
1264 : else
1265 : ifp = NULL;
1266 : } else {
1267 0 : ifp = if_lookup_by_name_all_vrf(ifname);
1268 : }
1269 0 : if (ifp)
1270 0 : VTY_PUSH_CONTEXT(INTERFACE_NODE, ifp);
1271 : }
1272 :
1273 : return ret;
1274 : }
1275 :
1276 0 : DEFPY_YANG (no_interface,
1277 : no_interface_cmd,
1278 : "no interface IFNAME [vrf NAME$vrf_name]",
1279 : NO_STR
1280 : "Delete a pseudo interface's configuration\n"
1281 : "Interface's name\n"
1282 : VRF_CMD_HELP_STR)
1283 : {
1284 0 : char xpath_list[XPATH_MAXLEN];
1285 0 : int count;
1286 :
1287 0 : if (vrf_is_backend_netns()) {
1288 : /*
1289 : * For backward compatibility, if the VRF name is not specified
1290 : * and there is exactly one interface with this name in the
1291 : * system, use its VRF. Otherwise fallback to the default VRF.
1292 : */
1293 0 : if (!vrf_name) {
1294 0 : count = vrfname_by_ifname(ifname, &vrf_name);
1295 0 : if (count != 1)
1296 0 : vrf_name = VRF_DEFAULT_NAME;
1297 : }
1298 :
1299 0 : snprintf(xpath_list, XPATH_MAXLEN,
1300 : "/frr-interface:lib/interface[name='%s:%s']", vrf_name,
1301 : ifname);
1302 : } else {
1303 0 : snprintf(xpath_list, XPATH_MAXLEN,
1304 : "/frr-interface:lib/interface[name='%s']", ifname);
1305 : }
1306 :
1307 0 : nb_cli_enqueue_change(vty, ".", NB_OP_DESTROY, NULL);
1308 :
1309 0 : return nb_cli_apply_changes(vty, "%s", xpath_list);
1310 : }
1311 :
1312 0 : static void netns_ifname_split(const char *xpath, char *ifname, char *vrfname)
1313 : {
1314 0 : char *delim;
1315 0 : int len;
1316 :
1317 0 : assert(vrf_is_backend_netns());
1318 :
1319 0 : delim = strchr(xpath, ':');
1320 0 : assert(delim);
1321 :
1322 0 : len = delim - xpath;
1323 0 : memcpy(vrfname, xpath, len);
1324 0 : vrfname[len] = 0;
1325 :
1326 0 : strlcpy(ifname, delim + 1, XPATH_MAXLEN);
1327 0 : }
1328 :
1329 0 : static void cli_show_interface(struct vty *vty, const struct lyd_node *dnode,
1330 : bool show_defaults)
1331 : {
1332 0 : vty_out(vty, "!\n");
1333 :
1334 0 : if (vrf_is_backend_netns()) {
1335 0 : char ifname[XPATH_MAXLEN];
1336 0 : char vrfname[XPATH_MAXLEN];
1337 :
1338 0 : netns_ifname_split(yang_dnode_get_string(dnode, "./name"),
1339 : ifname, vrfname);
1340 :
1341 0 : vty_out(vty, "interface %s", ifname);
1342 0 : if (!strmatch(vrfname, VRF_DEFAULT_NAME))
1343 0 : vty_out(vty, " vrf %s", vrfname);
1344 : } else {
1345 0 : const char *ifname = yang_dnode_get_string(dnode, "./name");
1346 :
1347 0 : vty_out(vty, "interface %s", ifname);
1348 : }
1349 :
1350 0 : vty_out(vty, "\n");
1351 0 : }
1352 :
1353 0 : static void cli_show_interface_end(struct vty *vty,
1354 : const struct lyd_node *dnode)
1355 : {
1356 0 : vty_out(vty, "exit\n");
1357 0 : }
1358 :
1359 0 : void if_vty_config_start(struct vty *vty, struct interface *ifp)
1360 : {
1361 0 : vty_frame(vty, "!\n");
1362 0 : vty_frame(vty, "interface %s", ifp->name);
1363 :
1364 0 : if (vrf_is_backend_netns() && strcmp(ifp->vrf->name, VRF_DEFAULT_NAME))
1365 0 : vty_frame(vty, " vrf %s", ifp->vrf->name);
1366 :
1367 0 : vty_frame(vty, "\n");
1368 0 : }
1369 :
1370 0 : void if_vty_config_end(struct vty *vty)
1371 : {
1372 0 : vty_endframe(vty, "exit\n!\n");
1373 0 : }
1374 :
1375 : /*
1376 : * XPath: /frr-interface:lib/interface/description
1377 : */
1378 0 : DEFPY_YANG (interface_desc,
1379 : interface_desc_cmd,
1380 : "description LINE...",
1381 : "Interface specific description\n"
1382 : "Characters describing this interface\n")
1383 : {
1384 0 : char *desc;
1385 0 : int ret;
1386 :
1387 0 : desc = argv_concat(argv, argc, 1);
1388 0 : nb_cli_enqueue_change(vty, "./description", NB_OP_MODIFY, desc);
1389 0 : ret = nb_cli_apply_changes(vty, NULL);
1390 0 : XFREE(MTYPE_TMP, desc);
1391 :
1392 0 : return ret;
1393 : }
1394 :
1395 0 : DEFPY_YANG (no_interface_desc,
1396 : no_interface_desc_cmd,
1397 : "no description",
1398 : NO_STR
1399 : "Interface specific description\n")
1400 : {
1401 0 : nb_cli_enqueue_change(vty, "./description", NB_OP_DESTROY, NULL);
1402 :
1403 0 : return nb_cli_apply_changes(vty, NULL);
1404 : }
1405 :
1406 0 : static void cli_show_interface_desc(struct vty *vty,
1407 : const struct lyd_node *dnode,
1408 : bool show_defaults)
1409 : {
1410 0 : vty_out(vty, " description %s\n", yang_dnode_get_string(dnode, NULL));
1411 0 : }
1412 :
1413 : /* Interface autocomplete. */
1414 0 : static void if_autocomplete(vector comps, struct cmd_token *token)
1415 : {
1416 0 : struct interface *ifp;
1417 0 : struct vrf *vrf;
1418 :
1419 0 : RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
1420 0 : FOR_ALL_INTERFACES (vrf, ifp) {
1421 0 : vector_set(comps, XSTRDUP(MTYPE_COMPLETION, ifp->name));
1422 : }
1423 : }
1424 0 : }
1425 :
1426 : static const struct cmd_variable_handler if_var_handlers[] = {
1427 : {/* "interface NAME" */
1428 : .varname = "interface",
1429 : .completions = if_autocomplete},
1430 : {.tokenname = "IFNAME", .completions = if_autocomplete},
1431 : {.tokenname = "INTERFACE", .completions = if_autocomplete},
1432 : {.completions = NULL}};
1433 :
1434 : static struct cmd_node interface_node = {
1435 : .name = "interface",
1436 : .node = INTERFACE_NODE,
1437 : .parent_node = CONFIG_NODE,
1438 : .prompt = "%s(config-if)# ",
1439 : };
1440 :
1441 0 : static int if_config_write_single(const struct lyd_node *dnode, void *arg)
1442 : {
1443 0 : nb_cli_show_dnode_cmds(arg, dnode, false);
1444 :
1445 0 : return YANG_ITER_CONTINUE;
1446 : }
1447 :
1448 0 : static int if_nb_config_write(struct vty *vty)
1449 : {
1450 0 : yang_dnode_iterate(if_config_write_single, vty, running_config->dnode,
1451 : "/frr-interface:lib/interface");
1452 0 : return 1;
1453 : }
1454 :
1455 4 : void if_cmd_init(int (*config_write)(struct vty *))
1456 : {
1457 4 : cmd_variable_handler_register(if_var_handlers);
1458 :
1459 4 : interface_node.config_write = config_write;
1460 4 : install_node(&interface_node);
1461 :
1462 4 : install_element(CONFIG_NODE, &interface_cmd);
1463 4 : install_element(CONFIG_NODE, &no_interface_cmd);
1464 :
1465 4 : install_default(INTERFACE_NODE);
1466 4 : install_element(INTERFACE_NODE, &interface_desc_cmd);
1467 4 : install_element(INTERFACE_NODE, &no_interface_desc_cmd);
1468 4 : }
1469 :
1470 0 : void if_cmd_init_default(void)
1471 : {
1472 0 : if_cmd_init(if_nb_config_write);
1473 0 : }
1474 :
1475 : /* ------- Northbound callbacks ------- */
1476 :
1477 : /*
1478 : * XPath: /frr-interface:lib/interface
1479 : */
1480 0 : static int lib_interface_create(struct nb_cb_create_args *args)
1481 : {
1482 0 : const char *ifname;
1483 0 : struct interface *ifp;
1484 :
1485 0 : ifname = yang_dnode_get_string(args->dnode, "./name");
1486 :
1487 0 : switch (args->event) {
1488 0 : case NB_EV_VALIDATE:
1489 0 : if (vrf_is_backend_netns()) {
1490 0 : char ifname_ns[XPATH_MAXLEN];
1491 0 : char vrfname_ns[XPATH_MAXLEN];
1492 :
1493 0 : netns_ifname_split(ifname, ifname_ns, vrfname_ns);
1494 :
1495 0 : if (strlen(ifname_ns) > 16) {
1496 0 : snprintf(
1497 : args->errmsg, args->errmsg_len,
1498 : "Maximum interface name length is 16 characters");
1499 0 : return NB_ERR_VALIDATION;
1500 : }
1501 0 : if (strlen(vrfname_ns) > 36) {
1502 0 : snprintf(
1503 : args->errmsg, args->errmsg_len,
1504 : "Maximum VRF name length is 36 characters");
1505 0 : return NB_ERR_VALIDATION;
1506 : }
1507 : } else {
1508 0 : if (strlen(ifname) > 16) {
1509 0 : snprintf(
1510 : args->errmsg, args->errmsg_len,
1511 : "Maximum interface name length is 16 characters");
1512 0 : return NB_ERR_VALIDATION;
1513 : }
1514 : }
1515 : break;
1516 : case NB_EV_PREPARE:
1517 : case NB_EV_ABORT:
1518 : break;
1519 0 : case NB_EV_APPLY:
1520 0 : if (vrf_is_backend_netns()) {
1521 0 : char ifname_ns[XPATH_MAXLEN];
1522 0 : char vrfname_ns[XPATH_MAXLEN];
1523 :
1524 0 : netns_ifname_split(ifname, ifname_ns, vrfname_ns);
1525 :
1526 0 : ifp = if_get_by_name(ifname_ns, VRF_UNKNOWN,
1527 : vrfname_ns);
1528 : } else {
1529 0 : ifp = if_get_by_name(ifname, VRF_UNKNOWN,
1530 : VRF_DEFAULT_NAME);
1531 : }
1532 :
1533 0 : ifp->configured = true;
1534 0 : nb_running_set_entry(args->dnode, ifp);
1535 0 : break;
1536 : }
1537 :
1538 : return NB_OK;
1539 : }
1540 :
1541 0 : static int lib_interface_destroy(struct nb_cb_destroy_args *args)
1542 : {
1543 0 : struct interface *ifp;
1544 0 : struct vrf *vrf;
1545 :
1546 0 : switch (args->event) {
1547 0 : case NB_EV_VALIDATE:
1548 0 : ifp = nb_running_get_entry(args->dnode, NULL, true);
1549 0 : if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) {
1550 0 : snprintf(args->errmsg, args->errmsg_len,
1551 : "only inactive interfaces can be deleted");
1552 0 : return NB_ERR_VALIDATION;
1553 : }
1554 : break;
1555 : case NB_EV_PREPARE:
1556 : case NB_EV_ABORT:
1557 : break;
1558 0 : case NB_EV_APPLY:
1559 0 : ifp = nb_running_unset_entry(args->dnode);
1560 0 : vrf = ifp->vrf;
1561 :
1562 0 : ifp->configured = false;
1563 0 : if_delete(&ifp);
1564 :
1565 0 : if (!vrf_is_enabled(vrf))
1566 0 : vrf_delete(vrf);
1567 : break;
1568 : }
1569 :
1570 : return NB_OK;
1571 : }
1572 :
1573 : /*
1574 : * XPath: /frr-interface:lib/interface
1575 : */
1576 0 : static const void *lib_interface_get_next(struct nb_cb_get_next_args *args)
1577 : {
1578 0 : struct vrf *vrf;
1579 0 : struct interface *pif = (struct interface *)args->list_entry;
1580 :
1581 0 : if (args->list_entry == NULL) {
1582 0 : vrf = RB_MIN(vrf_name_head, &vrfs_by_name);
1583 0 : assert(vrf);
1584 0 : pif = RB_MIN(if_name_head, &vrf->ifaces_by_name);
1585 : } else {
1586 0 : vrf = pif->vrf;
1587 0 : pif = RB_NEXT(if_name_head, pif);
1588 : /* if no more interfaces, switch to next vrf */
1589 0 : while (pif == NULL) {
1590 0 : vrf = RB_NEXT(vrf_name_head, vrf);
1591 0 : if (!vrf)
1592 : return NULL;
1593 0 : pif = RB_MIN(if_name_head, &vrf->ifaces_by_name);
1594 : }
1595 : }
1596 :
1597 : return pif;
1598 : }
1599 :
1600 0 : static int lib_interface_get_keys(struct nb_cb_get_keys_args *args)
1601 : {
1602 0 : const struct interface *ifp = args->list_entry;
1603 :
1604 0 : args->keys->num = 1;
1605 :
1606 0 : if (vrf_is_backend_netns())
1607 0 : snprintf(args->keys->key[0], sizeof(args->keys->key[0]),
1608 0 : "%s:%s", ifp->vrf->name, ifp->name);
1609 : else
1610 0 : snprintf(args->keys->key[0], sizeof(args->keys->key[0]), "%s",
1611 0 : ifp->name);
1612 :
1613 0 : return NB_OK;
1614 : }
1615 :
1616 : static const void *
1617 0 : lib_interface_lookup_entry(struct nb_cb_lookup_entry_args *args)
1618 : {
1619 0 : if (vrf_is_backend_netns()) {
1620 0 : char ifname[XPATH_MAXLEN];
1621 0 : char vrfname[XPATH_MAXLEN];
1622 0 : struct vrf *vrf;
1623 :
1624 0 : netns_ifname_split(args->keys->key[0], ifname, vrfname);
1625 :
1626 0 : vrf = vrf_lookup_by_name(vrfname);
1627 :
1628 0 : return vrf ? if_lookup_by_name(ifname, vrf->vrf_id) : NULL;
1629 : } else {
1630 0 : return if_lookup_by_name_all_vrf(args->keys->key[0]);
1631 : }
1632 : }
1633 :
1634 : /*
1635 : * XPath: /frr-interface:lib/interface/description
1636 : */
1637 0 : static int lib_interface_description_modify(struct nb_cb_modify_args *args)
1638 : {
1639 0 : struct interface *ifp;
1640 0 : const char *description;
1641 :
1642 0 : if (args->event != NB_EV_APPLY)
1643 : return NB_OK;
1644 :
1645 0 : ifp = nb_running_get_entry(args->dnode, NULL, true);
1646 0 : XFREE(MTYPE_IFDESC, ifp->desc);
1647 0 : description = yang_dnode_get_string(args->dnode, NULL);
1648 0 : ifp->desc = XSTRDUP(MTYPE_IFDESC, description);
1649 :
1650 0 : return NB_OK;
1651 : }
1652 :
1653 0 : static int lib_interface_description_destroy(struct nb_cb_destroy_args *args)
1654 : {
1655 0 : struct interface *ifp;
1656 :
1657 0 : if (args->event != NB_EV_APPLY)
1658 : return NB_OK;
1659 :
1660 0 : ifp = nb_running_get_entry(args->dnode, NULL, true);
1661 0 : XFREE(MTYPE_IFDESC, ifp->desc);
1662 :
1663 0 : return NB_OK;
1664 : }
1665 :
1666 : /*
1667 : * XPath: /frr-interface:lib/interface/vrf
1668 : */
1669 : static struct yang_data *
1670 0 : lib_interface_vrf_get_elem(struct nb_cb_get_elem_args *args)
1671 : {
1672 0 : const struct interface *ifp = args->list_entry;
1673 :
1674 0 : return yang_data_new_string(args->xpath, ifp->vrf->name);
1675 : }
1676 :
1677 : /*
1678 : * XPath: /frr-interface:lib/interface/state/if-index
1679 : */
1680 : static struct yang_data *
1681 0 : lib_interface_state_if_index_get_elem(struct nb_cb_get_elem_args *args)
1682 : {
1683 0 : const struct interface *ifp = args->list_entry;
1684 :
1685 0 : return yang_data_new_int32(args->xpath, ifp->ifindex);
1686 : }
1687 :
1688 : /*
1689 : * XPath: /frr-interface:lib/interface/state/mtu
1690 : */
1691 : static struct yang_data *
1692 0 : lib_interface_state_mtu_get_elem(struct nb_cb_get_elem_args *args)
1693 : {
1694 0 : const struct interface *ifp = args->list_entry;
1695 :
1696 0 : return yang_data_new_uint16(args->xpath, ifp->mtu);
1697 : }
1698 :
1699 : /*
1700 : * XPath: /frr-interface:lib/interface/state/mtu6
1701 : */
1702 : static struct yang_data *
1703 0 : lib_interface_state_mtu6_get_elem(struct nb_cb_get_elem_args *args)
1704 : {
1705 0 : const struct interface *ifp = args->list_entry;
1706 :
1707 0 : return yang_data_new_uint32(args->xpath, ifp->mtu6);
1708 : }
1709 :
1710 : /*
1711 : * XPath: /frr-interface:lib/interface/state/speed
1712 : */
1713 : static struct yang_data *
1714 0 : lib_interface_state_speed_get_elem(struct nb_cb_get_elem_args *args)
1715 : {
1716 0 : const struct interface *ifp = args->list_entry;
1717 :
1718 0 : return yang_data_new_uint32(args->xpath, ifp->speed);
1719 : }
1720 :
1721 : /*
1722 : * XPath: /frr-interface:lib/interface/state/metric
1723 : */
1724 : static struct yang_data *
1725 0 : lib_interface_state_metric_get_elem(struct nb_cb_get_elem_args *args)
1726 : {
1727 0 : const struct interface *ifp = args->list_entry;
1728 :
1729 0 : return yang_data_new_uint32(args->xpath, ifp->metric);
1730 : }
1731 :
1732 : /*
1733 : * XPath: /frr-interface:lib/interface/state/flags
1734 : */
1735 : static struct yang_data *
1736 0 : lib_interface_state_flags_get_elem(struct nb_cb_get_elem_args *args)
1737 : {
1738 : /* TODO: implement me. */
1739 0 : return NULL;
1740 : }
1741 :
1742 : /*
1743 : * XPath: /frr-interface:lib/interface/state/type
1744 : */
1745 : static struct yang_data *
1746 0 : lib_interface_state_type_get_elem(struct nb_cb_get_elem_args *args)
1747 : {
1748 : /* TODO: implement me. */
1749 0 : return NULL;
1750 : }
1751 :
1752 : /*
1753 : * XPath: /frr-interface:lib/interface/state/phy-address
1754 : */
1755 : static struct yang_data *
1756 0 : lib_interface_state_phy_address_get_elem(struct nb_cb_get_elem_args *args)
1757 : {
1758 0 : const struct interface *ifp = args->list_entry;
1759 0 : struct ethaddr macaddr;
1760 :
1761 0 : memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN);
1762 :
1763 0 : return yang_data_new_mac(args->xpath, &macaddr);
1764 : }
1765 :
1766 : /* clang-format off */
1767 : const struct frr_yang_module_info frr_interface_info = {
1768 : .name = "frr-interface",
1769 : .nodes = {
1770 : {
1771 : .xpath = "/frr-interface:lib/interface",
1772 : .cbs = {
1773 : .create = lib_interface_create,
1774 : .destroy = lib_interface_destroy,
1775 : .cli_show = cli_show_interface,
1776 : .cli_show_end = cli_show_interface_end,
1777 : .get_next = lib_interface_get_next,
1778 : .get_keys = lib_interface_get_keys,
1779 : .lookup_entry = lib_interface_lookup_entry,
1780 : },
1781 : },
1782 : {
1783 : .xpath = "/frr-interface:lib/interface/description",
1784 : .cbs = {
1785 : .modify = lib_interface_description_modify,
1786 : .destroy = lib_interface_description_destroy,
1787 : .cli_show = cli_show_interface_desc,
1788 : },
1789 : },
1790 : {
1791 : .xpath = "/frr-interface:lib/interface/vrf",
1792 : .cbs = {
1793 : .get_elem = lib_interface_vrf_get_elem,
1794 : }
1795 : },
1796 : {
1797 : .xpath = "/frr-interface:lib/interface/state/if-index",
1798 : .cbs = {
1799 : .get_elem = lib_interface_state_if_index_get_elem,
1800 : }
1801 : },
1802 : {
1803 : .xpath = "/frr-interface:lib/interface/state/mtu",
1804 : .cbs = {
1805 : .get_elem = lib_interface_state_mtu_get_elem,
1806 : }
1807 : },
1808 : {
1809 : .xpath = "/frr-interface:lib/interface/state/mtu6",
1810 : .cbs = {
1811 : .get_elem = lib_interface_state_mtu6_get_elem,
1812 : }
1813 : },
1814 : {
1815 : .xpath = "/frr-interface:lib/interface/state/speed",
1816 : .cbs = {
1817 : .get_elem = lib_interface_state_speed_get_elem,
1818 : }
1819 : },
1820 : {
1821 : .xpath = "/frr-interface:lib/interface/state/metric",
1822 : .cbs = {
1823 : .get_elem = lib_interface_state_metric_get_elem,
1824 : }
1825 : },
1826 : {
1827 : .xpath = "/frr-interface:lib/interface/state/flags",
1828 : .cbs = {
1829 : .get_elem = lib_interface_state_flags_get_elem,
1830 : }
1831 : },
1832 : {
1833 : .xpath = "/frr-interface:lib/interface/state/type",
1834 : .cbs = {
1835 : .get_elem = lib_interface_state_type_get_elem,
1836 : }
1837 : },
1838 : {
1839 : .xpath = "/frr-interface:lib/interface/state/phy-address",
1840 : .cbs = {
1841 : .get_elem = lib_interface_state_phy_address_get_elem,
1842 : }
1843 : },
1844 : {
1845 : .xpath = NULL,
1846 : },
1847 : }
1848 : };
|