]> git.proxmox.com Git - mirror_frr.git/blame - zebra/rt_netlink.c
Merge pull request #11426 from error2407/open_policy
[mirror_frr.git] / zebra / rt_netlink.c
CommitLineData
718e3744 1/* Kernel routing table updates using netlink over GNU/Linux system.
2 * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
3 *
4 * This file is part of GNU Zebra.
5 *
6 * GNU Zebra is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
896014f4
DL
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
718e3744 19 */
20
21#include <zebra.h>
ddfeb486
DL
22
23#ifdef HAVE_NETLINK
24
8689b25a
HS
25/* The following definition is to workaround an issue in the Linux kernel
26 * header files with redefinition of 'struct in6_addr' in both
27 * netinet/in.h and linux/in6.h.
28 * Reference - https://sourceware.org/ml/libc-alpha/2013-01/msg00599.html
29 */
30#define _LINUX_IN6_H
31
8ccc7e80 32#include <net/if_arp.h>
ba777396
RW
33#include <linux/lwtunnel.h>
34#include <linux/mpls_iptunnel.h>
8689b25a
HS
35#include <linux/seg6_iptunnel.h>
36#include <linux/seg6_local.h>
ba777396
RW
37#include <linux/neighbour.h>
38#include <linux/rtnetlink.h>
d9f5b2f5 39#include <linux/nexthop.h>
718e3744 40
41/* Hack for GNU libc version 2. */
42#ifndef MSG_TRUNC
43#define MSG_TRUNC 0x20
44#endif /* MSG_TRUNC */
45
46#include "linklist.h"
47#include "if.h"
48#include "log.h"
49#include "prefix.h"
8689b25a
HS
50#include "plist.h"
51#include "plist_int.h"
718e3744 52#include "connected.h"
53#include "table.h"
26e2ae36 54#include "memory.h"
718e3744 55#include "rib.h"
e04ab74d 56#include "thread.h"
edd7c245 57#include "privs.h"
fb018d25 58#include "nexthop.h"
78104b9b 59#include "vrf.h"
5e6a74d8 60#include "vty.h"
40c7bdb0 61#include "mpls.h"
13d60d35 62#include "vxlan.h"
8d03bc50 63#include "printfrr.h"
718e3744 64
bf094f69 65#include "zebra/zapi_msg.h"
fe18ee2d 66#include "zebra/zebra_ns.h"
7c551956 67#include "zebra/zebra_vrf.h"
6621ca86 68#include "zebra/rt.h"
718e3744 69#include "zebra/redistribute.h"
70#include "zebra/interface.h"
71#include "zebra/debug.h"
12f6fb97 72#include "zebra/rtadv.h"
567b877d 73#include "zebra/zebra_ptm.h"
40c7bdb0 74#include "zebra/zebra_mpls.h"
1fdc9eae 75#include "zebra/kernel_netlink.h"
76#include "zebra/rt_netlink.h"
d9f5b2f5 77#include "zebra/zebra_nhg.h"
e3be0432 78#include "zebra/zebra_mroute.h"
2232a77c 79#include "zebra/zebra_vxlan.h"
364fed6b 80#include "zebra/zebra_errors.h"
506efd37 81#include "zebra/zebra_evpn_mh.h"
1d80c209 82#include "zebra/zebra_trace.h"
e3be0432 83
40c7bdb0 84#ifndef AF_MPLS
85#define AF_MPLS 28
86#endif
87
d87ed8d7
AK
88/* Re-defining as I am unable to include <linux/if_bridge.h> which has the
89 * UAPI for MAC sync. */
90#ifndef _UAPI_LINUX_IF_BRIDGE_H
4bcdb608 91#define BR_SPH_LIST_SIZE 10
d87ed8d7
AK
92#endif
93
2232a77c 94static vlanid_t filter_vlan = 0;
95
7c99d51b
MS
96/* We capture whether the current kernel supports nexthop ids; by
97 * default, we'll use them if possible. There's also a configuration
98 * available to _disable_ use of kernel nexthops.
99 */
fec211ad 100static bool supports_nh;
81505946 101
d62a17ae 102struct gw_family_t {
d7c0a89a
QY
103 uint16_t filler;
104 uint16_t family;
d62a17ae 105 union g_addr gate;
40c7bdb0 106};
107
2b64873d
DL
108static const char ipv4_ll_buf[16] = "169.254.0.1";
109static struct in_addr ipv4_ll;
8755598a 110
002e5c43
SW
111/* Is this a ipv4 over ipv6 route? */
112static bool is_route_v4_over_v6(unsigned char rtm_family,
113 enum nexthop_types_t nexthop_type)
114{
115 if (rtm_family == AF_INET
116 && (nexthop_type == NEXTHOP_TYPE_IPV6
117 || nexthop_type == NEXTHOP_TYPE_IPV6_IFINDEX))
118 return true;
119
120 return false;
121}
122
7c99d51b
MS
123/* Helper to control use of kernel-level nexthop ids */
124static bool kernel_nexthops_supported(void)
125{
d982012a
SW
126 return (supports_nh && !vrf_is_backend_netns()
127 && zebra_nhg_kernel_nexthops_enabled());
7c99d51b
MS
128}
129
6c67f41f
SW
130/*
131 * Some people may only want to use NHGs created by protos and not
132 * implicitly created by Zebra. This check accounts for that.
133 */
134static bool proto_nexthops_only(void)
135{
136 return zebra_nhg_proto_nexthops_only();
137}
138
139/* Is this a proto created NHG? */
140static bool is_proto_nhg(uint32_t id, int type)
141{
142 /* If type is available, use it as the source of truth */
143 if (type) {
144 if (type != ZEBRA_ROUTE_NHG)
145 return true;
146 return false;
147 }
148
54c89c93 149 if (id >= ZEBRA_NHG_PROTO_LOWER)
6c67f41f
SW
150 return true;
151
152 return false;
153}
154
8755598a
DS
155/*
156 * The ipv4_ll data structure is used for all 5549
157 * additions to the kernel. Let's figure out the
158 * correct value one time instead for every
159 * install/remove of a 5549 type route
160 */
d62a17ae 161void rt_netlink_init(void)
8755598a 162{
d62a17ae 163 inet_pton(AF_INET, ipv4_ll_buf, &ipv4_ll);
8755598a
DS
164}
165
931fa60c
MS
166/*
167 * Mapping from dataplane neighbor flags to netlink flags
168 */
169static uint8_t neigh_flags_to_netlink(uint8_t dplane_flags)
170{
171 uint8_t flags = 0;
172
173 if (dplane_flags & DPLANE_NTF_EXT_LEARNED)
174 flags |= NTF_EXT_LEARNED;
175 if (dplane_flags & DPLANE_NTF_ROUTER)
176 flags |= NTF_ROUTER;
d68e74b4
JU
177 if (dplane_flags & DPLANE_NTF_USE)
178 flags |= NTF_USE;
931fa60c
MS
179
180 return flags;
181}
182
183/*
184 * Mapping from dataplane neighbor state to netlink state
185 */
186static uint16_t neigh_state_to_netlink(uint16_t dplane_state)
187{
188 uint16_t state = 0;
189
190 if (dplane_state & DPLANE_NUD_REACHABLE)
191 state |= NUD_REACHABLE;
192 if (dplane_state & DPLANE_NUD_STALE)
193 state |= NUD_STALE;
194 if (dplane_state & DPLANE_NUD_NOARP)
195 state |= NUD_NOARP;
196 if (dplane_state & DPLANE_NUD_PROBE)
197 state |= NUD_PROBE;
d68e74b4
JU
198 if (dplane_state & DPLANE_NUD_INCOMPLETE)
199 state |= NUD_INCOMPLETE;
0a27a2fe
PG
200 if (dplane_state & DPLANE_NUD_PERMANENT)
201 state |= NUD_PERMANENT;
202 if (dplane_state & DPLANE_NUD_FAILED)
203 state |= NUD_FAILED;
931fa60c
MS
204
205 return state;
206}
207
208
6a6d11a3 209static inline bool is_selfroute(int proto)
23b1f334 210{
d62a17ae 211 if ((proto == RTPROT_BGP) || (proto == RTPROT_OSPF)
d4d71f11 212 || (proto == RTPROT_ZSTATIC) || (proto == RTPROT_ZEBRA)
d62a17ae 213 || (proto == RTPROT_ISIS) || (proto == RTPROT_RIPNG)
214 || (proto == RTPROT_NHRP) || (proto == RTPROT_EIGRP)
915902cb 215 || (proto == RTPROT_LDP) || (proto == RTPROT_BABEL)
0761368a 216 || (proto == RTPROT_RIP) || (proto == RTPROT_SHARP)
31f937fb
SM
217 || (proto == RTPROT_PBR) || (proto == RTPROT_OPENFABRIC)
218 || (proto == RTPROT_SRTE)) {
6a6d11a3 219 return true;
d62a17ae 220 }
221
6a6d11a3 222 return false;
23b1f334
DD
223}
224
915902cb 225static inline int zebra2proto(int proto)
23b1f334 226{
d62a17ae 227 switch (proto) {
228 case ZEBRA_ROUTE_BABEL:
229 proto = RTPROT_BABEL;
230 break;
231 case ZEBRA_ROUTE_BGP:
232 proto = RTPROT_BGP;
233 break;
234 case ZEBRA_ROUTE_OSPF:
235 case ZEBRA_ROUTE_OSPF6:
236 proto = RTPROT_OSPF;
237 break;
238 case ZEBRA_ROUTE_STATIC:
d4d71f11 239 proto = RTPROT_ZSTATIC;
d62a17ae 240 break;
241 case ZEBRA_ROUTE_ISIS:
242 proto = RTPROT_ISIS;
243 break;
244 case ZEBRA_ROUTE_RIP:
245 proto = RTPROT_RIP;
246 break;
247 case ZEBRA_ROUTE_RIPNG:
248 proto = RTPROT_RIPNG;
249 break;
250 case ZEBRA_ROUTE_NHRP:
251 proto = RTPROT_NHRP;
252 break;
253 case ZEBRA_ROUTE_EIGRP:
254 proto = RTPROT_EIGRP;
255 break;
256 case ZEBRA_ROUTE_LDP:
257 proto = RTPROT_LDP;
258 break;
8a71d93d
DS
259 case ZEBRA_ROUTE_SHARP:
260 proto = RTPROT_SHARP;
261 break;
0761368a
DS
262 case ZEBRA_ROUTE_PBR:
263 proto = RTPROT_PBR;
264 break;
da82f6b4
CF
265 case ZEBRA_ROUTE_OPENFABRIC:
266 proto = RTPROT_OPENFABRIC;
267 break;
31f937fb
SM
268 case ZEBRA_ROUTE_SRTE:
269 proto = RTPROT_SRTE;
270 break;
a56ec5c0 271 case ZEBRA_ROUTE_TABLE:
38e40db1 272 case ZEBRA_ROUTE_NHG:
a56ec5c0
DS
273 proto = RTPROT_ZEBRA;
274 break;
911d4d48
DE
275 case ZEBRA_ROUTE_CONNECT:
276 case ZEBRA_ROUTE_KERNEL:
277 proto = RTPROT_KERNEL;
278 break;
d62a17ae 279 default:
0761368a
DS
280 /*
281 * When a user adds a new protocol this will show up
282 * to let them know to do something about it. This
283 * is intentionally a warn because we should see
284 * this as part of development of a new protocol
285 */
9df414fe
QY
286 zlog_debug(
287 "%s: Please add this protocol(%d) to proper rt_netlink.c handling",
15569c58 288 __func__, proto);
d62a17ae 289 proto = RTPROT_ZEBRA;
290 break;
291 }
292
293 return proto;
23b1f334
DD
294}
295
38e40db1 296static inline int proto2zebra(int proto, int family, bool is_nexthop)
915902cb
DS
297{
298 switch (proto) {
299 case RTPROT_BABEL:
300 proto = ZEBRA_ROUTE_BABEL;
301 break;
302 case RTPROT_BGP:
303 proto = ZEBRA_ROUTE_BGP;
304 break;
305 case RTPROT_OSPF:
d6816f68
DS
306 proto = (family == AF_INET) ? ZEBRA_ROUTE_OSPF
307 : ZEBRA_ROUTE_OSPF6;
915902cb
DS
308 break;
309 case RTPROT_ISIS:
310 proto = ZEBRA_ROUTE_ISIS;
311 break;
312 case RTPROT_RIP:
313 proto = ZEBRA_ROUTE_RIP;
314 break;
315 case RTPROT_RIPNG:
316 proto = ZEBRA_ROUTE_RIPNG;
317 break;
318 case RTPROT_NHRP:
319 proto = ZEBRA_ROUTE_NHRP;
320 break;
321 case RTPROT_EIGRP:
322 proto = ZEBRA_ROUTE_EIGRP;
323 break;
324 case RTPROT_LDP:
325 proto = ZEBRA_ROUTE_LDP;
326 break;
327 case RTPROT_STATIC:
d4d71f11 328 case RTPROT_ZSTATIC:
915902cb
DS
329 proto = ZEBRA_ROUTE_STATIC;
330 break;
0761368a
DS
331 case RTPROT_SHARP:
332 proto = ZEBRA_ROUTE_SHARP;
333 break;
334 case RTPROT_PBR:
335 proto = ZEBRA_ROUTE_PBR;
336 break;
da82f6b4
CF
337 case RTPROT_OPENFABRIC:
338 proto = ZEBRA_ROUTE_OPENFABRIC;
339 break;
31f937fb
SM
340 case RTPROT_SRTE:
341 proto = ZEBRA_ROUTE_SRTE;
342 break;
38e40db1
SW
343 case RTPROT_ZEBRA:
344 if (is_nexthop) {
345 proto = ZEBRA_ROUTE_NHG;
346 break;
347 }
348 /* Intentional fall thru */
915902cb 349 default:
0761368a
DS
350 /*
351 * When a user adds a new protocol this will show up
352 * to let them know to do something about it. This
353 * is intentionally a warn because we should see
354 * this as part of development of a new protocol
355 */
9df414fe
QY
356 zlog_debug(
357 "%s: Please add this protocol(%d) to proper rt_netlink.c handling",
15569c58 358 __func__, proto);
915902cb
DS
359 proto = ZEBRA_ROUTE_KERNEL;
360 break;
361 }
362 return proto;
363}
364
12f6fb97
DS
365/*
366Pending: create an efficient table_id (in a tree/hash) based lookup)
367 */
9d866c07 368vrf_id_t vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id)
12f6fb97 369{
d62a17ae 370 struct vrf *vrf;
371 struct zebra_vrf *zvrf;
12f6fb97 372
a2addae8 373 RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
78dd30b2
PG
374 zvrf = vrf->info;
375 if (zvrf == NULL)
d62a17ae 376 continue;
78dd30b2
PG
377 /* case vrf with netns : match the netnsid */
378 if (vrf_is_backend_netns()) {
379 if (ns_id == zvrf_id(zvrf))
380 return zvrf_id(zvrf);
381 } else {
382 /* VRF is VRF_BACKEND_VRF_LITE */
383 if (zvrf->table_id != table_id)
384 continue;
385 return zvrf_id(zvrf);
386 }
d62a17ae 387 }
12f6fb97 388
d62a17ae 389 return VRF_DEFAULT;
12f6fb97
DS
390}
391
87da6a60
SW
392/**
393 * @parse_encap_mpls() - Parses encapsulated mpls attributes
394 * @tb: Pointer to rtattr to look for nested items in.
395 * @labels: Pointer to store labels in.
396 *
397 * Return: Number of mpls labels found.
398 */
399static int parse_encap_mpls(struct rtattr *tb, mpls_label_t *labels)
400{
401 struct rtattr *tb_encap[MPLS_IPTUNNEL_MAX + 1] = {0};
402 mpls_lse_t *lses = NULL;
403 int num_labels = 0;
404 uint32_t ttl = 0;
405 uint32_t bos = 0;
406 uint32_t exp = 0;
407 mpls_label_t label = 0;
408
409 netlink_parse_rtattr_nested(tb_encap, MPLS_IPTUNNEL_MAX, tb);
410 lses = (mpls_lse_t *)RTA_DATA(tb_encap[MPLS_IPTUNNEL_DST]);
411 while (!bos && num_labels < MPLS_MAX_LABELS) {
412 mpls_lse_decode(lses[num_labels], &label, &ttl, &exp, &bos);
413 labels[num_labels++] = label;
414 }
415
416 return num_labels;
417}
418
d49e6c4a
HS
419static enum seg6local_action_t
420parse_encap_seg6local(struct rtattr *tb,
421 struct seg6local_context *ctx)
422{
1bda3e62 423 struct rtattr *tb_encap[256] = {};
d49e6c4a
HS
424 enum seg6local_action_t act = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC;
425
426 netlink_parse_rtattr_nested(tb_encap, 256, tb);
427
428 if (tb_encap[SEG6_LOCAL_ACTION])
429 act = *(uint32_t *)RTA_DATA(tb_encap[SEG6_LOCAL_ACTION]);
430
431 if (tb_encap[SEG6_LOCAL_NH4])
432 ctx->nh4 = *(struct in_addr *)RTA_DATA(
433 tb_encap[SEG6_LOCAL_NH4]);
434
435 if (tb_encap[SEG6_LOCAL_NH6])
436 ctx->nh6 = *(struct in6_addr *)RTA_DATA(
437 tb_encap[SEG6_LOCAL_NH6]);
438
439 if (tb_encap[SEG6_LOCAL_TABLE])
440 ctx->table = *(uint32_t *)RTA_DATA(tb_encap[SEG6_LOCAL_TABLE]);
441
7eab60a7
RS
442 if (tb_encap[SEG6_LOCAL_VRFTABLE])
443 ctx->table =
444 *(uint32_t *)RTA_DATA(tb_encap[SEG6_LOCAL_VRFTABLE]);
445
d49e6c4a
HS
446 return act;
447}
448
f16de90b
HS
449static int parse_encap_seg6(struct rtattr *tb, struct in6_addr *segs)
450{
1bda3e62 451 struct rtattr *tb_encap[256] = {};
f16de90b
HS
452 struct seg6_iptunnel_encap *ipt = NULL;
453 struct in6_addr *segments = NULL;
454
455 netlink_parse_rtattr_nested(tb_encap, 256, tb);
456
457 /*
458 * TODO: It's not support multiple SID list.
459 */
460 if (tb_encap[SEG6_IPTUNNEL_SRH]) {
461 ipt = (struct seg6_iptunnel_encap *)
462 RTA_DATA(tb_encap[SEG6_IPTUNNEL_SRH]);
463 segments = ipt->srh[0].segments;
464 *segs = segments[0];
465 return 1;
466 }
467
468 return 0;
469}
470
471
77a44d94
SW
472static struct nexthop
473parse_nexthop_unicast(ns_id_t ns_id, struct rtmsg *rtm, struct rtattr **tb,
474 enum blackhole_type bh_type, int index, void *prefsrc,
20822f9d 475 void *gate, afi_t afi, vrf_id_t vrf_id)
77a44d94
SW
476{
477 struct interface *ifp = NULL;
478 struct nexthop nh = {0};
479 mpls_label_t labels[MPLS_MAX_LABELS] = {0};
480 int num_labels = 0;
b9596f13 481 enum seg6local_action_t seg6l_act = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC;
1bda3e62
HS
482 struct seg6local_context seg6l_ctx = {};
483 struct in6_addr seg6_segs = {};
f16de90b 484 int num_segs = 0;
77a44d94 485
20822f9d 486 vrf_id_t nh_vrf_id = vrf_id;
77a44d94
SW
487 size_t sz = (afi == AFI_IP) ? 4 : 16;
488
489 if (bh_type == BLACKHOLE_UNSPEC) {
490 if (index && !gate)
491 nh.type = NEXTHOP_TYPE_IFINDEX;
492 else if (index && gate)
493 nh.type = (afi == AFI_IP) ? NEXTHOP_TYPE_IPV4_IFINDEX
494 : NEXTHOP_TYPE_IPV6_IFINDEX;
495 else if (!index && gate)
496 nh.type = (afi == AFI_IP) ? NEXTHOP_TYPE_IPV4
497 : NEXTHOP_TYPE_IPV6;
498 else {
499 nh.type = NEXTHOP_TYPE_BLACKHOLE;
500 nh.bh_type = bh_type;
501 }
502 } else {
503 nh.type = NEXTHOP_TYPE_BLACKHOLE;
504 nh.bh_type = bh_type;
505 }
506 nh.ifindex = index;
507 if (prefsrc)
508 memcpy(&nh.src, prefsrc, sz);
509 if (gate)
510 memcpy(&nh.gate, gate, sz);
511
512 if (index) {
513 ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), index);
514 if (ifp)
096f7609 515 nh_vrf_id = ifp->vrf->vrf_id;
77a44d94
SW
516 }
517 nh.vrf_id = nh_vrf_id;
518
519 if (tb[RTA_ENCAP] && tb[RTA_ENCAP_TYPE]
520 && *(uint16_t *)RTA_DATA(tb[RTA_ENCAP_TYPE])
521 == LWTUNNEL_ENCAP_MPLS) {
522 num_labels = parse_encap_mpls(tb[RTA_ENCAP], labels);
523 }
d49e6c4a
HS
524 if (tb[RTA_ENCAP] && tb[RTA_ENCAP_TYPE]
525 && *(uint16_t *)RTA_DATA(tb[RTA_ENCAP_TYPE])
526 == LWTUNNEL_ENCAP_SEG6_LOCAL) {
527 seg6l_act = parse_encap_seg6local(tb[RTA_ENCAP], &seg6l_ctx);
528 }
f16de90b
HS
529 if (tb[RTA_ENCAP] && tb[RTA_ENCAP_TYPE]
530 && *(uint16_t *)RTA_DATA(tb[RTA_ENCAP_TYPE])
531 == LWTUNNEL_ENCAP_SEG6) {
532 num_segs = parse_encap_seg6(tb[RTA_ENCAP], &seg6_segs);
533 }
77a44d94
SW
534
535 if (rtm->rtm_flags & RTNH_F_ONLINK)
536 SET_FLAG(nh.flags, NEXTHOP_FLAG_ONLINK);
537
c704cb44
DS
538 if (rtm->rtm_flags & RTNH_F_LINKDOWN)
539 SET_FLAG(nh.flags, NEXTHOP_FLAG_LINKDOWN);
540
77a44d94
SW
541 if (num_labels)
542 nexthop_add_labels(&nh, ZEBRA_LSP_STATIC, num_labels, labels);
543
d49e6c4a 544 if (seg6l_act != ZEBRA_SEG6_LOCAL_ACTION_UNSPEC)
eab0f8f0 545 nexthop_add_srv6_seg6local(&nh, seg6l_act, &seg6l_ctx);
d49e6c4a 546
f16de90b 547 if (num_segs)
eab0f8f0 548 nexthop_add_srv6_seg6(&nh, &seg6_segs);
f16de90b 549
77a44d94
SW
550 return nh;
551}
552
20822f9d 553static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id,
0eb97b86 554 struct nexthop_group *ng,
20822f9d
SW
555 struct rtmsg *rtm,
556 struct rtnexthop *rtnh,
557 struct rtattr **tb,
558 void *prefsrc, vrf_id_t vrf_id)
559{
560 void *gate = NULL;
561 struct interface *ifp = NULL;
562 int index = 0;
563 /* MPLS labels */
564 mpls_label_t labels[MPLS_MAX_LABELS] = {0};
565 int num_labels = 0;
b9596f13 566 enum seg6local_action_t seg6l_act = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC;
1bda3e62
HS
567 struct seg6local_context seg6l_ctx = {};
568 struct in6_addr seg6_segs = {};
f16de90b 569 int num_segs = 0;
20822f9d
SW
570 struct rtattr *rtnh_tb[RTA_MAX + 1] = {};
571
572 int len = RTA_PAYLOAD(tb[RTA_MULTIPATH]);
573 vrf_id_t nh_vrf_id = vrf_id;
574
20822f9d
SW
575 for (;;) {
576 struct nexthop *nh = NULL;
577
578 if (len < (int)sizeof(*rtnh) || rtnh->rtnh_len > len)
579 break;
580
581 index = rtnh->rtnh_ifindex;
582 if (index) {
583 /*
584 * Yes we are looking this up
585 * for every nexthop and just
586 * using the last one looked
587 * up right now
588 */
589 ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id),
590 index);
591 if (ifp)
096f7609 592 nh_vrf_id = ifp->vrf->vrf_id;
20822f9d
SW
593 else {
594 flog_warn(
595 EC_ZEBRA_UNKNOWN_INTERFACE,
596 "%s: Unknown interface %u specified, defaulting to VRF_DEFAULT",
15569c58 597 __func__, index);
20822f9d
SW
598 nh_vrf_id = VRF_DEFAULT;
599 }
600 } else
601 nh_vrf_id = vrf_id;
602
603 if (rtnh->rtnh_len > sizeof(*rtnh)) {
20822f9d
SW
604 netlink_parse_rtattr(rtnh_tb, RTA_MAX, RTNH_DATA(rtnh),
605 rtnh->rtnh_len - sizeof(*rtnh));
606 if (rtnh_tb[RTA_GATEWAY])
607 gate = RTA_DATA(rtnh_tb[RTA_GATEWAY]);
608 if (rtnh_tb[RTA_ENCAP] && rtnh_tb[RTA_ENCAP_TYPE]
609 && *(uint16_t *)RTA_DATA(rtnh_tb[RTA_ENCAP_TYPE])
610 == LWTUNNEL_ENCAP_MPLS) {
611 num_labels = parse_encap_mpls(
612 rtnh_tb[RTA_ENCAP], labels);
613 }
d49e6c4a
HS
614 if (rtnh_tb[RTA_ENCAP] && rtnh_tb[RTA_ENCAP_TYPE]
615 && *(uint16_t *)RTA_DATA(rtnh_tb[RTA_ENCAP_TYPE])
616 == LWTUNNEL_ENCAP_SEG6_LOCAL) {
617 seg6l_act = parse_encap_seg6local(
618 rtnh_tb[RTA_ENCAP], &seg6l_ctx);
619 }
f16de90b
HS
620 if (rtnh_tb[RTA_ENCAP] && rtnh_tb[RTA_ENCAP_TYPE]
621 && *(uint16_t *)RTA_DATA(rtnh_tb[RTA_ENCAP_TYPE])
622 == LWTUNNEL_ENCAP_SEG6) {
623 num_segs = parse_encap_seg6(rtnh_tb[RTA_ENCAP],
624 &seg6_segs);
625 }
20822f9d
SW
626 }
627
f3354e16
SW
628 if (gate && rtm->rtm_family == AF_INET) {
629 if (index)
0eb97b86
MS
630 nh = nexthop_from_ipv4_ifindex(
631 gate, prefsrc, index, nh_vrf_id);
f3354e16 632 else
0eb97b86
MS
633 nh = nexthop_from_ipv4(gate, prefsrc,
634 nh_vrf_id);
f3354e16
SW
635 } else if (gate && rtm->rtm_family == AF_INET6) {
636 if (index)
0eb97b86
MS
637 nh = nexthop_from_ipv6_ifindex(
638 gate, index, nh_vrf_id);
f3354e16 639 else
0eb97b86 640 nh = nexthop_from_ipv6(gate, nh_vrf_id);
20822f9d 641 } else
0eb97b86 642 nh = nexthop_from_ifindex(index, nh_vrf_id);
20822f9d
SW
643
644 if (nh) {
df7fb580
DS
645 nh->weight = rtnh->rtnh_hops + 1;
646
20822f9d
SW
647 if (num_labels)
648 nexthop_add_labels(nh, ZEBRA_LSP_STATIC,
649 num_labels, labels);
650
d49e6c4a 651 if (seg6l_act != ZEBRA_SEG6_LOCAL_ACTION_UNSPEC)
eab0f8f0
HS
652 nexthop_add_srv6_seg6local(nh, seg6l_act,
653 &seg6l_ctx);
d49e6c4a 654
f16de90b 655 if (num_segs)
eab0f8f0 656 nexthop_add_srv6_seg6(nh, &seg6_segs);
f16de90b 657
20822f9d
SW
658 if (rtnh->rtnh_flags & RTNH_F_ONLINK)
659 SET_FLAG(nh->flags, NEXTHOP_FLAG_ONLINK);
0eb97b86
MS
660
661 /* Add to temporary list */
662 nexthop_group_add_sorted(ng, nh);
20822f9d
SW
663 }
664
665 if (rtnh->rtnh_len == 0)
666 break;
667
668 len -= NLMSG_ALIGN(rtnh->rtnh_len);
669 rtnh = RTNH_NEXT(rtnh);
670 }
671
0eb97b86 672 uint8_t nhop_num = nexthop_group_nexthop_num(ng);
20822f9d
SW
673
674 return nhop_num;
675}
676
718e3744 677/* Looking up routing table by netlink interface. */
2414abd3 678static int netlink_route_change_read_unicast(struct nlmsghdr *h, ns_id_t ns_id,
d62a17ae 679 int startup)
718e3744 680{
d62a17ae 681 int len;
682 struct rtmsg *rtm;
683 struct rtattr *tb[RTA_MAX + 1];
acde7f6b 684 uint32_t flags = 0;
d62a17ae 685 struct prefix p;
792fa92e 686 struct prefix_ipv6 src_p = {};
78dd30b2 687 vrf_id_t vrf_id;
6a6d11a3 688 bool selfroute;
d62a17ae 689
690 char anyaddr[16] = {0};
691
915902cb 692 int proto = ZEBRA_ROUTE_KERNEL;
d62a17ae 693 int index = 0;
694 int table;
695 int metric = 0;
d7c0a89a 696 uint32_t mtu = 0;
25715c7e 697 uint8_t distance = 0;
4e40b6d6 698 route_tag_t tag = 0;
fcc89a9c 699 uint32_t nhe_id = 0;
d62a17ae 700
701 void *dest = NULL;
702 void *gate = NULL;
703 void *prefsrc = NULL; /* IPv4 preferred source host address */
704 void *src = NULL; /* IPv6 srcdest source prefix */
e655a03c 705 enum blackhole_type bh_type = BLACKHOLE_UNSPEC;
d62a17ae 706
3cee2135
DS
707 frrtrace(3, frr_zebra, netlink_route_change_read_unicast, h, ns_id,
708 startup);
709
d62a17ae 710 rtm = NLMSG_DATA(h);
711
712 if (startup && h->nlmsg_type != RTM_NEWROUTE)
713 return 0;
e655a03c
DL
714 switch (rtm->rtm_type) {
715 case RTN_UNICAST:
716 break;
717 case RTN_BLACKHOLE:
718 bh_type = BLACKHOLE_NULL;
719 break;
720 case RTN_UNREACHABLE:
721 bh_type = BLACKHOLE_REJECT;
722 break;
723 case RTN_PROHIBIT:
724 bh_type = BLACKHOLE_ADMINPROHIB;
725 break;
726 default:
8c8f250b
DS
727 if (IS_ZEBRA_DEBUG_KERNEL)
728 zlog_debug("Route rtm_type: %s(%d) intentionally ignoring",
729 nl_rttype_to_str(rtm->rtm_type),
730 rtm->rtm_type);
d62a17ae 731 return 0;
e655a03c 732 }
d62a17ae 733
734 len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg));
9bdf8618 735 if (len < 0) {
15569c58
DA
736 zlog_err(
737 "%s: Message received from netlink is of a broken size %d %zu",
738 __func__, h->nlmsg_len,
739 (size_t)NLMSG_LENGTH(sizeof(struct rtmsg)));
d62a17ae 740 return -1;
9bdf8618 741 }
d62a17ae 742
d62a17ae 743 netlink_parse_rtattr(tb, RTA_MAX, RTM_RTA(rtm), len);
744
745 if (rtm->rtm_flags & RTM_F_CLONED)
746 return 0;
747 if (rtm->rtm_protocol == RTPROT_REDIRECT)
748 return 0;
749 if (rtm->rtm_protocol == RTPROT_KERNEL)
750 return 0;
751
6a6d11a3
NN
752 selfroute = is_selfroute(rtm->rtm_protocol);
753
e4876266
DS
754 if (!startup && selfroute
755 && h->nlmsg_type == RTM_NEWROUTE
756 && !zrouter.asic_offloaded) {
6ab5222f
DS
757 if (IS_ZEBRA_DEBUG_KERNEL)
758 zlog_debug("Route type: %d Received that we think we have originated, ignoring",
759 rtm->rtm_protocol);
d62a17ae 760 return 0;
6ab5222f 761 }
d62a17ae 762
763 /* We don't care about change notifications for the MPLS table. */
764 /* TODO: Revisit this. */
765 if (rtm->rtm_family == AF_MPLS)
766 return 0;
767
768 /* Table corresponding to route. */
769 if (tb[RTA_TABLE])
770 table = *(int *)RTA_DATA(tb[RTA_TABLE]);
771 else
772 table = rtm->rtm_table;
773
774 /* Map to VRF */
78dd30b2 775 vrf_id = vrf_lookup_by_table(table, ns_id);
d62a17ae 776 if (vrf_id == VRF_DEFAULT) {
777 if (!is_zebra_valid_kernel_table(table)
778 && !is_zebra_main_routing_table(table))
779 return 0;
780 }
781
5a3cf853
DS
782 if (rtm->rtm_flags & RTM_F_TRAP)
783 flags |= ZEBRA_FLAG_TRAPPED;
784 if (rtm->rtm_flags & RTM_F_OFFLOAD)
785 flags |= ZEBRA_FLAG_OFFLOADED;
0d32fbee
DS
786 if (rtm->rtm_flags & RTM_F_OFFLOAD_FAILED)
787 flags |= ZEBRA_FLAG_OFFLOAD_FAILED;
5a3cf853 788
d62a17ae 789 /* Route which inserted by Zebra. */
6a6d11a3 790 if (selfroute) {
d62a17ae 791 flags |= ZEBRA_FLAG_SELFROUTE;
38e40db1 792 proto = proto2zebra(rtm->rtm_protocol, rtm->rtm_family, false);
915902cb 793 }
d62a17ae 794 if (tb[RTA_OIF])
795 index = *(int *)RTA_DATA(tb[RTA_OIF]);
796
797 if (tb[RTA_DST])
798 dest = RTA_DATA(tb[RTA_DST]);
799 else
800 dest = anyaddr;
801
802 if (tb[RTA_SRC])
803 src = RTA_DATA(tb[RTA_SRC]);
804 else
805 src = anyaddr;
806
807 if (tb[RTA_PREFSRC])
808 prefsrc = RTA_DATA(tb[RTA_PREFSRC]);
809
810 if (tb[RTA_GATEWAY])
811 gate = RTA_DATA(tb[RTA_GATEWAY]);
812
fcc89a9c
SW
813 if (tb[RTA_NH_ID])
814 nhe_id = *(uint32_t *)RTA_DATA(tb[RTA_NH_ID]);
815
f19435a8
DS
816 if (tb[RTA_PRIORITY])
817 metric = *(int *)RTA_DATA(tb[RTA_PRIORITY]);
d62a17ae 818
4e40b6d6
KK
819#if defined(SUPPORT_REALMS)
820 if (tb[RTA_FLOW])
821 tag = *(uint32_t *)RTA_DATA(tb[RTA_FLOW]);
822#endif
823
f19435a8
DS
824 if (tb[RTA_METRICS]) {
825 struct rtattr *mxrta[RTAX_MAX + 1];
d62a17ae 826
996c9314 827 netlink_parse_rtattr(mxrta, RTAX_MAX, RTA_DATA(tb[RTA_METRICS]),
f19435a8 828 RTA_PAYLOAD(tb[RTA_METRICS]));
d62a17ae 829
f19435a8 830 if (mxrta[RTAX_MTU])
d7c0a89a 831 mtu = *(uint32_t *)RTA_DATA(mxrta[RTAX_MTU]);
d62a17ae 832 }
833
834 if (rtm->rtm_family == AF_INET) {
835 p.family = AF_INET;
930571d2 836 if (rtm->rtm_dst_len > IPV4_MAX_BITLEN) {
e17d9b2d 837 zlog_err(
75829703 838 "Invalid destination prefix length: %u received from kernel route change",
930571d2 839 rtm->rtm_dst_len);
e17d9b2d 840 return -1;
930571d2 841 }
d62a17ae 842 memcpy(&p.u.prefix4, dest, 4);
843 p.prefixlen = rtm->rtm_dst_len;
844
1f610a1f 845 if (rtm->rtm_src_len != 0) {
9df414fe 846 flog_warn(
e914ccbe 847 EC_ZEBRA_UNSUPPORTED_V4_SRCDEST,
2dbe669b
DA
848 "unsupported IPv4 sourcedest route (dest %pFX vrf %u)",
849 &p, vrf_id);
1f610a1f
CF
850 return 0;
851 }
930571d2 852
1f610a1f
CF
853 /* Force debug below to not display anything for source */
854 src_p.prefixlen = 0;
d62a17ae 855 } else if (rtm->rtm_family == AF_INET6) {
856 p.family = AF_INET6;
930571d2 857 if (rtm->rtm_dst_len > IPV6_MAX_BITLEN) {
e17d9b2d 858 zlog_err(
75829703 859 "Invalid destination prefix length: %u received from kernel route change",
930571d2 860 rtm->rtm_dst_len);
e17d9b2d 861 return -1;
930571d2 862 }
d62a17ae 863 memcpy(&p.u.prefix6, dest, 16);
864 p.prefixlen = rtm->rtm_dst_len;
865
866 src_p.family = AF_INET6;
930571d2 867 if (rtm->rtm_src_len > IPV6_MAX_BITLEN) {
e17d9b2d 868 zlog_err(
75829703 869 "Invalid source prefix length: %u received from kernel route change",
930571d2 870 rtm->rtm_src_len);
e17d9b2d 871 return -1;
930571d2 872 }
d62a17ae 873 memcpy(&src_p.prefix, src, 16);
874 src_p.prefixlen = rtm->rtm_src_len;
deb28338
MS
875 } else {
876 /* We only handle the AFs we handle... */
877 if (IS_ZEBRA_DEBUG_KERNEL)
878 zlog_debug("%s: unknown address-family %u", __func__,
879 rtm->rtm_family);
880 return 0;
d62a17ae 881 }
882
25715c7e
DS
883 /*
884 * For ZEBRA_ROUTE_KERNEL types:
885 *
886 * The metric/priority of the route received from the kernel
887 * is a 32 bit number. We are going to interpret the high
888 * order byte as the Admin Distance and the low order 3 bytes
889 * as the metric.
890 *
891 * This will allow us to do two things:
892 * 1) Allow the creation of kernel routes that can be
893 * overridden by zebra.
894 * 2) Allow the old behavior for 'most' kernel route types
895 * if a user enters 'ip route ...' v4 routes get a metric
896 * of 0 and v6 routes get a metric of 1024. Both of these
897 * values will end up with a admin distance of 0, which
898 * will cause them to win for the purposes of zebra.
899 */
900 if (proto == ZEBRA_ROUTE_KERNEL) {
901 distance = (metric >> 24) & 0xFF;
996c9314 902 metric = (metric & 0x00FFFFFF);
25715c7e
DS
903 }
904
d62a17ae 905 if (IS_ZEBRA_DEBUG_KERNEL) {
d62a17ae 906 char buf2[PREFIX_STRLEN];
2dbe669b 907
bd47f3a3 908 zlog_debug(
2dbe669b
DA
909 "%s %pFX%s%s vrf %s(%u) table_id: %u metric: %d Admin Distance: %d",
910 nl_msg_type_to_str(h->nlmsg_type), &p,
bd47f3a3
JU
911 src_p.prefixlen ? " from " : "",
912 src_p.prefixlen ? prefix2str(&src_p, buf2, sizeof(buf2))
913 : "",
914 vrf_id_to_name(vrf_id), vrf_id, table, metric,
915 distance);
d62a17ae 916 }
917
918 afi_t afi = AFI_IP;
919 if (rtm->rtm_family == AF_INET6)
920 afi = AFI_IP6;
921
922 if (h->nlmsg_type == RTM_NEWROUTE) {
8795f904 923
fd36be7e 924 if (!tb[RTA_MULTIPATH]) {
77a44d94 925 struct nexthop nh = {0};
8795f904 926
77a44d94
SW
927 if (!nhe_id) {
928 nh = parse_nexthop_unicast(
929 ns_id, rtm, tb, bh_type, index, prefsrc,
20822f9d 930 gate, afi, vrf_id);
87da6a60 931 }
4a7371e9 932 rib_add(afi, SAFI_UNICAST, vrf_id, proto, 0, flags, &p,
8032b717 933 &src_p, &nh, nhe_id, table, metric, mtu,
c6eee91f 934 distance, tag, startup);
fd36be7e 935 } else {
d62a17ae 936 /* This is a multipath route */
d62a17ae 937 struct route_entry *re;
0eb97b86 938 struct nexthop_group *ng = NULL;
d62a17ae 939 struct rtnexthop *rtnh =
940 (struct rtnexthop *)RTA_DATA(tb[RTA_MULTIPATH]);
d62a17ae 941
942 re = XCALLOC(MTYPE_RE, sizeof(struct route_entry));
915902cb 943 re->type = proto;
25715c7e 944 re->distance = distance;
d62a17ae 945 re->flags = flags;
946 re->metric = metric;
947 re->mtu = mtu;
948 re->vrf_id = vrf_id;
949 re->table = table;
98572489 950 re->uptime = monotime(NULL);
4e40b6d6 951 re->tag = tag;
bbb322f2 952 re->nhe_id = nhe_id;
3c04071d 953
20822f9d 954 if (!nhe_id) {
0eb97b86
MS
955 uint8_t nhop_num;
956
957 /* Use temporary list of nexthops; parse
958 * message payload's nexthops.
959 */
960 ng = nexthop_group_new();
961 nhop_num =
20822f9d 962 parse_multipath_nexthops_unicast(
0eb97b86 963 ns_id, ng, rtm, rtnh, tb,
20822f9d
SW
964 prefsrc, vrf_id);
965
966 zserv_nexthop_num_warn(
967 __func__, (const struct prefix *)&p,
968 nhop_num);
0eb97b86
MS
969
970 if (nhop_num == 0) {
971 nexthop_group_delete(&ng);
972 ng = NULL;
973 }
d62a17ae 974 }
975
0eb97b86 976 if (nhe_id || ng)
1f610a1f 977 rib_add_multipath(afi, SAFI_UNICAST, &p,
c6eee91f 978 &src_p, re, ng, startup);
20822f9d
SW
979 else
980 XFREE(MTYPE_RE, re);
d62a17ae 981 }
982 } else {
bc541126
SW
983 if (nhe_id) {
984 rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0, flags,
985 &p, &src_p, NULL, nhe_id, table, metric,
3ceae22b 986 distance, true);
bc541126
SW
987 } else {
988 if (!tb[RTA_MULTIPATH]) {
989 struct nexthop nh;
760f39dc
HS
990
991 nh = parse_nexthop_unicast(
992 ns_id, rtm, tb, bh_type, index, prefsrc,
993 gate, afi, vrf_id);
bc541126
SW
994 rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0,
995 flags, &p, &src_p, &nh, 0, table,
3ceae22b 996 metric, distance, true);
8ba5bd58 997 } else {
bc541126
SW
998 /* XXX: need to compare the entire list of
999 * nexthops here for NLM_F_APPEND stupidity */
1000 rib_delete(afi, SAFI_UNICAST, vrf_id, proto, 0,
1001 flags, &p, &src_p, NULL, 0, table,
3ceae22b 1002 metric, distance, true);
8ba5bd58 1003 }
d62a17ae 1004 }
1005 }
1006
1007 return 0;
718e3744 1008}
1009
e3be0432
DS
1010static struct mcast_route_data *mroute = NULL;
1011
2414abd3 1012static int netlink_route_change_read_multicast(struct nlmsghdr *h,
d62a17ae 1013 ns_id_t ns_id, int startup)
565fdc75 1014{
d62a17ae 1015 int len;
1016 struct rtmsg *rtm;
1017 struct rtattr *tb[RTA_MAX + 1];
1018 struct mcast_route_data *m;
1019 struct mcast_route_data mr;
1020 int iif = 0;
1021 int count;
1022 int oif[256];
1023 int oif_count = 0;
d62a17ae 1024 char oif_list[256] = "\0";
78dd30b2 1025 vrf_id_t vrf;
43b5cc5e 1026 int table;
d62a17ae 1027
1028 if (mroute)
1029 m = mroute;
1030 else {
1031 memset(&mr, 0, sizeof(mr));
1032 m = &mr;
1033 }
1034
1035 rtm = NLMSG_DATA(h);
1036
1037 len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg));
1038
d62a17ae 1039 netlink_parse_rtattr(tb, RTA_MAX, RTM_RTA(rtm), len);
90d82769 1040
43b5cc5e
DS
1041 if (tb[RTA_TABLE])
1042 table = *(int *)RTA_DATA(tb[RTA_TABLE]);
1043 else
1044 table = rtm->rtm_table;
1045
78dd30b2 1046 vrf = vrf_lookup_by_table(table, ns_id);
43b5cc5e 1047
d62a17ae 1048 if (tb[RTA_IIF])
1049 iif = *(int *)RTA_DATA(tb[RTA_IIF]);
1050
4d3b4b18
MR
1051 if (tb[RTA_SRC]) {
1052 if (rtm->rtm_family == RTNL_FAMILY_IPMR)
1053 m->src.ipaddr_v4 =
1054 *(struct in_addr *)RTA_DATA(tb[RTA_SRC]);
1055 else
1056 m->src.ipaddr_v6 =
1057 *(struct in6_addr *)RTA_DATA(tb[RTA_SRC]);
1058 }
d62a17ae 1059
4d3b4b18
MR
1060 if (tb[RTA_DST]) {
1061 if (rtm->rtm_family == RTNL_FAMILY_IPMR)
1062 m->grp.ipaddr_v4 =
1063 *(struct in_addr *)RTA_DATA(tb[RTA_DST]);
1064 else
1065 m->grp.ipaddr_v6 =
1066 *(struct in6_addr *)RTA_DATA(tb[RTA_DST]);
1067 }
d62a17ae 1068
62819462 1069 if (tb[RTA_EXPIRES])
d62a17ae 1070 m->lastused = *(unsigned long long *)RTA_DATA(tb[RTA_EXPIRES]);
1071
1072 if (tb[RTA_MULTIPATH]) {
1073 struct rtnexthop *rtnh =
1074 (struct rtnexthop *)RTA_DATA(tb[RTA_MULTIPATH]);
1075
1076 len = RTA_PAYLOAD(tb[RTA_MULTIPATH]);
1077 for (;;) {
1078 if (len < (int)sizeof(*rtnh) || rtnh->rtnh_len > len)
1079 break;
1080
1081 oif[oif_count] = rtnh->rtnh_ifindex;
1082 oif_count++;
1083
3c04071d
SW
1084 if (rtnh->rtnh_len == 0)
1085 break;
1086
d62a17ae 1087 len -= NLMSG_ALIGN(rtnh->rtnh_len);
1088 rtnh = RTNH_NEXT(rtnh);
1089 }
1090 }
1091
4d3b4b18
MR
1092 if (rtm->rtm_family == RTNL_FAMILY_IPMR) {
1093 SET_IPADDR_V4(&m->src);
1094 SET_IPADDR_V4(&m->grp);
1095 } else if (rtm->rtm_family == RTNL_FAMILY_IP6MR) {
1096 SET_IPADDR_V6(&m->src);
1097 SET_IPADDR_V6(&m->grp);
1098 } else {
1099 zlog_warn("%s: Invalid rtm_family received", __func__);
1100 return 0;
1101 }
1102
d62a17ae 1103 if (IS_ZEBRA_DEBUG_KERNEL) {
822c9af2
SW
1104 struct interface *ifp = NULL;
1105 struct zebra_vrf *zvrf = NULL;
1106
d62a17ae 1107 for (count = 0; count < oif_count; count++) {
1108 ifp = if_lookup_by_index(oif[count], vrf);
1109 char temp[256];
1110
772270f3
QY
1111 snprintf(temp, sizeof(temp), "%s(%d) ",
1112 ifp ? ifp->name : "Unknown", oif[count]);
eab4a5c2 1113 strlcat(oif_list, temp, sizeof(oif_list));
d62a17ae 1114 }
822c9af2 1115 zvrf = zebra_vrf_lookup_by_id(vrf);
d62a17ae 1116 ifp = if_lookup_by_index(iif, vrf);
822c9af2 1117 zlog_debug(
4d3b4b18 1118 "MCAST VRF: %s(%d) %s (%pIA,%pIA) IIF: %s(%d) OIF: %s jiffies: %lld",
bd47f3a3 1119 zvrf_name(zvrf), vrf, nl_msg_type_to_str(h->nlmsg_type),
4d3b4b18
MR
1120 &m->src, &m->grp, ifp ? ifp->name : "Unknown", iif,
1121 oif_list, m->lastused);
90d82769 1122 }
d62a17ae 1123 return 0;
565fdc75
DS
1124}
1125
2414abd3 1126int netlink_route_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
565fdc75 1127{
d62a17ae 1128 int len;
d62a17ae 1129 struct rtmsg *rtm;
1130
1131 rtm = NLMSG_DATA(h);
1132
1133 if (!(h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)) {
1134 /* If this is not route add/delete message print warning. */
9165c5f5 1135 zlog_debug("Kernel message: %s NS %u",
87b5d1b0 1136 nl_msg_type_to_str(h->nlmsg_type), ns_id);
d62a17ae 1137 return 0;
1138 }
1139
c25e2f1a
DS
1140 if (!(rtm->rtm_family == AF_INET ||
1141 rtm->rtm_family == AF_INET6 ||
1142 rtm->rtm_family == RTNL_FAMILY_IPMR )) {
9df414fe 1143 flog_warn(
e914ccbe 1144 EC_ZEBRA_UNKNOWN_FAMILY,
87b5d1b0
DS
1145 "Invalid address family: %u received from kernel route change: %s",
1146 rtm->rtm_family, nl_msg_type_to_str(h->nlmsg_type));
8a1b681c
SW
1147 return 0;
1148 }
1149
d62a17ae 1150 /* Connected route. */
1151 if (IS_ZEBRA_DEBUG_KERNEL)
78dd30b2 1152 zlog_debug("%s %s %s proto %s NS %u",
d62a17ae 1153 nl_msg_type_to_str(h->nlmsg_type),
1154 nl_family_to_str(rtm->rtm_family),
1155 nl_rttype_to_str(rtm->rtm_type),
78dd30b2 1156 nl_rtproto_to_str(rtm->rtm_protocol), ns_id);
d62a17ae 1157
d62a17ae 1158
1159 len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct rtmsg));
9bdf8618 1160 if (len < 0) {
15569c58
DA
1161 zlog_err(
1162 "%s: Message received from netlink is of a broken size: %d %zu",
1163 __func__, h->nlmsg_len,
1164 (size_t)NLMSG_LENGTH(sizeof(struct rtmsg)));
d62a17ae 1165 return -1;
9bdf8618 1166 }
d62a17ae 1167
e655a03c 1168 if (rtm->rtm_type == RTN_MULTICAST)
2414abd3 1169 netlink_route_change_read_multicast(h, ns_id, startup);
e655a03c 1170 else
2414abd3 1171 netlink_route_change_read_unicast(h, ns_id, startup);
d62a17ae 1172 return 0;
565fdc75
DS
1173}
1174
289602d7 1175/* Request for specific route information from the kernel */
d62a17ae 1176static int netlink_request_route(struct zebra_ns *zns, int family, int type)
289602d7 1177{
d62a17ae 1178 struct {
1179 struct nlmsghdr n;
1180 struct rtmsg rtm;
1181 } req;
1182
1183 /* Form the request, specifying filter (rtattr) if needed. */
1184 memset(&req, 0, sizeof(req));
1185 req.n.nlmsg_type = type;
718f9b0f 1186 req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
d62a17ae 1187 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
1188 req.rtm.rtm_family = family;
1189
fd3f8e52 1190 return netlink_request(&zns->netlink_cmd, &req);
289602d7 1191}
1192
718e3744 1193/* Routing table read function using netlink interface. Only called
1194 bootstrap time. */
d62a17ae 1195int netlink_route_read(struct zebra_ns *zns)
718e3744 1196{
d62a17ae 1197 int ret;
85a75f1e
MS
1198 struct zebra_dplane_info dp_info;
1199
1200 zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/);
d62a17ae 1201
1202 /* Get IPv4 routing table. */
1203 ret = netlink_request_route(zns, AF_INET, RTM_GETROUTE);
1204 if (ret < 0)
1205 return ret;
1206 ret = netlink_parse_info(netlink_route_change_read_unicast,
9bfadae8 1207 &zns->netlink_cmd, &dp_info, 0, true);
d62a17ae 1208 if (ret < 0)
1209 return ret;
1210
1211 /* Get IPv6 routing table. */
1212 ret = netlink_request_route(zns, AF_INET6, RTM_GETROUTE);
1213 if (ret < 0)
1214 return ret;
1215 ret = netlink_parse_info(netlink_route_change_read_unicast,
9bfadae8 1216 &zns->netlink_cmd, &dp_info, 0, true);
d62a17ae 1217 if (ret < 0)
1218 return ret;
1219
1220 return 0;
718e3744 1221}
1222
0be6e7d7
JU
1223/*
1224 * The function returns true if the gateway info could be added
1225 * to the message, otherwise false is returned.
1226 */
1227static bool _netlink_route_add_gateway_info(uint8_t route_family,
312a6bee
JU
1228 uint8_t gw_family,
1229 struct nlmsghdr *nlmsg,
1230 size_t req_size, int bytelen,
1231 const struct nexthop *nexthop)
40c7bdb0 1232{
d62a17ae 1233 if (route_family == AF_MPLS) {
1234 struct gw_family_t gw_fam;
1235
1236 gw_fam.family = gw_family;
1237 if (gw_family == AF_INET)
1238 memcpy(&gw_fam.gate.ipv4, &nexthop->gate.ipv4, bytelen);
1239 else
1240 memcpy(&gw_fam.gate.ipv6, &nexthop->gate.ipv6, bytelen);
0be6e7d7
JU
1241 if (!nl_attr_put(nlmsg, req_size, RTA_VIA, &gw_fam.family,
1242 bytelen + 2))
1243 return false;
d62a17ae 1244 } else {
92d6f769
K
1245 if (!(nexthop->rparent
1246 && IS_MAPPED_IPV6(&nexthop->rparent->gate.ipv6))) {
1247 if (gw_family == AF_INET) {
1248 if (!nl_attr_put(nlmsg, req_size, RTA_GATEWAY,
1249 &nexthop->gate.ipv4, bytelen))
1250 return false;
1251 } else {
1252 if (!nl_attr_put(nlmsg, req_size, RTA_GATEWAY,
1253 &nexthop->gate.ipv6, bytelen))
1254 return false;
1255 }
0be6e7d7 1256 }
d62a17ae 1257 }
0be6e7d7
JU
1258
1259 return true;
40c7bdb0 1260}
1261
b7537db6
SW
1262static int build_label_stack(struct mpls_label_stack *nh_label,
1263 mpls_lse_t *out_lse, char *label_buf,
1264 size_t label_buf_size)
1265{
1266 char label_buf1[20];
1267 int num_labels = 0;
1268
1269 for (int i = 0; nh_label && i < nh_label->num_labels; i++) {
1270 if (nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL)
1271 continue;
1272
1273 if (IS_ZEBRA_DEBUG_KERNEL) {
1274 if (!num_labels)
7533cad7
QY
1275 snprintf(label_buf, label_buf_size, "label %u",
1276 nh_label->label[i]);
b7537db6 1277 else {
772270f3
QY
1278 snprintf(label_buf1, sizeof(label_buf1), "/%u",
1279 nh_label->label[i]);
b7537db6
SW
1280 strlcat(label_buf, label_buf1, label_buf_size);
1281 }
1282 }
1283
1284 out_lse[num_labels] =
1285 mpls_lse_encode(nh_label->label[i], 0, 0, 0);
1286 num_labels++;
1287 }
1288
1289 return num_labels;
1290}
1291
a757997c
JU
1292static bool _netlink_route_encode_label_info(struct mpls_label_stack *nh_label,
1293 struct nlmsghdr *nlmsg,
1294 size_t buflen, struct rtmsg *rtmsg,
1295 char *label_buf,
1296 size_t label_buf_size)
fa713d9e 1297{
d62a17ae 1298 mpls_lse_t out_lse[MPLS_MAX_LABELS];
a757997c 1299 int num_labels;
bd47f3a3 1300
d62a17ae 1301 /*
1302 * label_buf is *only* currently used within debugging.
1303 * As such when we assign it we are guarding it inside
1304 * a debug test. If you want to change this make sure
1305 * you fix this assumption
1306 */
1307 label_buf[0] = '\0';
d62a17ae 1308
a757997c
JU
1309 num_labels =
1310 build_label_stack(nh_label, out_lse, label_buf, label_buf_size);
fa712963
RW
1311
1312 if (num_labels) {
1313 /* Set the BoS bit */
1314 out_lse[num_labels - 1] |= htonl(1 << MPLS_LS_S_SHIFT);
1315
0be6e7d7 1316 if (rtmsg->rtm_family == AF_MPLS) {
a757997c 1317 if (!nl_attr_put(nlmsg, buflen, RTA_NEWDST, &out_lse,
0be6e7d7
JU
1318 num_labels * sizeof(mpls_lse_t)))
1319 return false;
1320 } else {
fa712963 1321 struct rtattr *nest;
fa712963 1322
a757997c
JU
1323 if (!nl_attr_put16(nlmsg, buflen, RTA_ENCAP_TYPE,
1324 LWTUNNEL_ENCAP_MPLS))
0be6e7d7
JU
1325 return false;
1326
a757997c 1327 nest = nl_attr_nest(nlmsg, buflen, RTA_ENCAP);
0be6e7d7
JU
1328 if (!nest)
1329 return false;
1330
a757997c 1331 if (!nl_attr_put(nlmsg, buflen, MPLS_IPTUNNEL_DST,
0be6e7d7
JU
1332 &out_lse,
1333 num_labels * sizeof(mpls_lse_t)))
1334 return false;
312a6bee 1335 nl_attr_nest_end(nlmsg, nest);
66d42727 1336 }
0aabccc0 1337 }
fa713d9e 1338
a757997c
JU
1339 return true;
1340}
1341
1342static bool _netlink_route_encode_nexthop_src(const struct nexthop *nexthop,
1343 int family,
1344 struct nlmsghdr *nlmsg,
1345 size_t buflen, int bytelen)
1346{
1347 if (family == AF_INET) {
1348 if (nexthop->rmap_src.ipv4.s_addr != INADDR_ANY) {
1349 if (!nl_attr_put(nlmsg, buflen, RTA_PREFSRC,
1350 &nexthop->rmap_src.ipv4, bytelen))
1351 return false;
1352 } else if (nexthop->src.ipv4.s_addr != INADDR_ANY) {
1353 if (!nl_attr_put(nlmsg, buflen, RTA_PREFSRC,
1354 &nexthop->src.ipv4, bytelen))
1355 return false;
1356 }
1357 } else if (family == AF_INET6) {
1358 if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->rmap_src.ipv6)) {
1359 if (!nl_attr_put(nlmsg, buflen, RTA_PREFSRC,
1360 &nexthop->rmap_src.ipv6, bytelen))
1361 return false;
1362 } else if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->src.ipv6)) {
1363 if (!nl_attr_put(nlmsg, buflen, RTA_PREFSRC,
1364 &nexthop->src.ipv6, bytelen))
1365 return false;
1366 }
1367 }
1368
1369 return true;
1370}
1371
f463eac7
HS
1372static ssize_t fill_seg6ipt_encap(char *buffer, size_t buflen,
1373 const struct in6_addr *seg)
76fb7ae4
HS
1374{
1375 struct seg6_iptunnel_encap *ipt;
1376 struct ipv6_sr_hdr *srh;
1377 const size_t srhlen = 24;
4df9d859 1378
f463eac7
HS
1379 /*
1380 * Caution: Support only SINGLE-SID, not MULTI-SID
1381 * This function only supports the case where segs represents
1382 * a single SID. If you want to extend the SRv6 functionality,
1383 * you should improve the Boundary Check.
1384 * Ex. In case of set a SID-List include multiple-SIDs as an
1385 * argument of the Transit Behavior, we must support variable
1386 * boundary check for buflen.
1387 */
1388 if (buflen < (sizeof(struct seg6_iptunnel_encap) +
1389 sizeof(struct ipv6_sr_hdr) + 16))
1390 return -1;
1391
76fb7ae4
HS
1392 memset(buffer, 0, buflen);
1393
1394 ipt = (struct seg6_iptunnel_encap *)buffer;
1395 ipt->mode = SEG6_IPTUN_MODE_ENCAP;
1396 srh = ipt->srh;
1397 srh->hdrlen = (srhlen >> 3) - 1;
1398 srh->type = 4;
1399 srh->segments_left = 0;
1400 srh->first_segment = 0;
1401 memcpy(&srh->segments[0], seg, sizeof(struct in6_addr));
1402
1403 return srhlen + 4;
1404}
1405
a757997c
JU
1406/* This function takes a nexthop as argument and adds
1407 * the appropriate netlink attributes to an existing
1408 * netlink message.
1409 *
1410 * @param routedesc: Human readable description of route type
1411 * (direct/recursive, single-/multipath)
1412 * @param bytelen: Length of addresses in bytes.
1413 * @param nexthop: Nexthop information
1414 * @param nlmsg: nlmsghdr structure to fill in.
1415 * @param req_size: The size allocated for the message.
1416 *
1417 * The function returns true if the nexthop could be added
1418 * to the message, otherwise false is returned.
1419 */
1420static bool _netlink_route_build_singlepath(const struct prefix *p,
1421 const char *routedesc, int bytelen,
1422 const struct nexthop *nexthop,
1423 struct nlmsghdr *nlmsg,
1424 struct rtmsg *rtmsg,
1425 size_t req_size, int cmd)
1426{
1427
1428 char label_buf[256];
1429 struct vrf *vrf;
1430 char addrstr[INET6_ADDRSTRLEN];
1431
1432 assert(nexthop);
1433
1434 vrf = vrf_lookup_by_id(nexthop->vrf_id);
1435
1436 if (!_netlink_route_encode_label_info(nexthop->nh_label, nlmsg,
1437 req_size, rtmsg, label_buf,
1438 sizeof(label_buf)))
1439 return false;
1440
eab0f8f0
HS
1441 if (nexthop->nh_srv6) {
1442 if (nexthop->nh_srv6->seg6local_action !=
1443 ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) {
1444 struct rtattr *nest;
1445 const struct seg6local_context *ctx;
52026569 1446
eab0f8f0
HS
1447 ctx = &nexthop->nh_srv6->seg6local_ctx;
1448 if (!nl_attr_put16(nlmsg, req_size, RTA_ENCAP_TYPE,
1449 LWTUNNEL_ENCAP_SEG6_LOCAL))
52026569 1450 return false;
eab0f8f0
HS
1451
1452 nest = nl_attr_nest(nlmsg, req_size, RTA_ENCAP);
1453 if (!nest)
52026569 1454 return false;
eab0f8f0
HS
1455
1456 switch (nexthop->nh_srv6->seg6local_action) {
1457 case ZEBRA_SEG6_LOCAL_ACTION_END:
1458 if (!nl_attr_put32(nlmsg, req_size,
1459 SEG6_LOCAL_ACTION,
1460 SEG6_LOCAL_ACTION_END))
1461 return false;
1462 break;
1463 case ZEBRA_SEG6_LOCAL_ACTION_END_X:
1464 if (!nl_attr_put32(nlmsg, req_size,
1465 SEG6_LOCAL_ACTION,
1466 SEG6_LOCAL_ACTION_END_X))
1467 return false;
1468 if (!nl_attr_put(nlmsg, req_size,
1469 SEG6_LOCAL_NH6, &ctx->nh6,
1470 sizeof(struct in6_addr)))
1471 return false;
1472 break;
1473 case ZEBRA_SEG6_LOCAL_ACTION_END_T:
1474 if (!nl_attr_put32(nlmsg, req_size,
1475 SEG6_LOCAL_ACTION,
1476 SEG6_LOCAL_ACTION_END_T))
1477 return false;
1478 if (!nl_attr_put32(nlmsg, req_size,
1479 SEG6_LOCAL_TABLE,
1480 ctx->table))
1481 return false;
1482 break;
1483 case ZEBRA_SEG6_LOCAL_ACTION_END_DX4:
1484 if (!nl_attr_put32(nlmsg, req_size,
1485 SEG6_LOCAL_ACTION,
1486 SEG6_LOCAL_ACTION_END_DX4))
1487 return false;
1488 if (!nl_attr_put(nlmsg, req_size,
1489 SEG6_LOCAL_NH4, &ctx->nh4,
1490 sizeof(struct in_addr)))
1491 return false;
1492 break;
1493 case ZEBRA_SEG6_LOCAL_ACTION_END_DT6:
1494 if (!nl_attr_put32(nlmsg, req_size,
1495 SEG6_LOCAL_ACTION,
1496 SEG6_LOCAL_ACTION_END_DT6))
1497 return false;
1498 if (!nl_attr_put32(nlmsg, req_size,
1499 SEG6_LOCAL_TABLE,
1500 ctx->table))
1501 return false;
1502 break;
7eab60a7
RS
1503 case ZEBRA_SEG6_LOCAL_ACTION_END_DT4:
1504 if (!nl_attr_put32(nlmsg, req_size,
1505 SEG6_LOCAL_ACTION,
1506 SEG6_LOCAL_ACTION_END_DT4))
1507 return false;
1508 if (!nl_attr_put32(nlmsg, req_size,
1509 SEG6_LOCAL_VRFTABLE,
1510 ctx->table))
1511 return false;
1512 break;
eab0f8f0
HS
1513 default:
1514 zlog_err("%s: unsupport seg6local behaviour action=%u",
1515 __func__,
1516 nexthop->nh_srv6->seg6local_action);
0a543b79 1517 return false;
eab0f8f0
HS
1518 }
1519 nl_attr_nest_end(nlmsg, nest);
1520 }
1521
1522 if (!sid_zero(&nexthop->nh_srv6->seg6_segs)) {
1523 char tun_buf[4096];
1524 ssize_t tun_len;
1525 struct rtattr *nest;
1526
1527 if (!nl_attr_put16(nlmsg, req_size, RTA_ENCAP_TYPE,
1528 LWTUNNEL_ENCAP_SEG6))
52026569 1529 return false;
eab0f8f0
HS
1530 nest = nl_attr_nest(nlmsg, req_size, RTA_ENCAP);
1531 if (!nest)
52026569 1532 return false;
eab0f8f0
HS
1533 tun_len = fill_seg6ipt_encap(tun_buf, sizeof(tun_buf),
1534 &nexthop->nh_srv6->seg6_segs);
1535 if (tun_len < 0)
52026569 1536 return false;
eab0f8f0
HS
1537 if (!nl_attr_put(nlmsg, req_size, SEG6_IPTUNNEL_SRH,
1538 tun_buf, tun_len))
52026569 1539 return false;
eab0f8f0 1540 nl_attr_nest_end(nlmsg, nest);
8689b25a 1541 }
76fb7ae4
HS
1542 }
1543
d62a17ae 1544 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK))
1545 rtmsg->rtm_flags |= RTNH_F_ONLINK;
1546
002e5c43 1547 if (is_route_v4_over_v6(rtmsg->rtm_family, nexthop->type)) {
d62a17ae 1548 rtmsg->rtm_flags |= RTNH_F_ONLINK;
0be6e7d7
JU
1549 if (!nl_attr_put(nlmsg, req_size, RTA_GATEWAY, &ipv4_ll, 4))
1550 return false;
1551 if (!nl_attr_put32(nlmsg, req_size, RTA_OIF, nexthop->ifindex))
1552 return false;
d62a17ae 1553
a757997c
JU
1554 if (cmd == RTM_NEWROUTE) {
1555 if (!_netlink_route_encode_nexthop_src(
1556 nexthop, AF_INET, nlmsg, req_size, bytelen))
0be6e7d7
JU
1557 return false;
1558 }
d62a17ae 1559
1560 if (IS_ZEBRA_DEBUG_KERNEL)
9266b315
RZ
1561 zlog_debug("%s: 5549 (%s): %pFX nexthop via %s %s if %u vrf %s(%u)",
1562 __func__, routedesc, p, ipv4_ll_buf,
1563 label_buf, nexthop->ifindex,
1564 VRF_LOGNAME(vrf), nexthop->vrf_id);
0be6e7d7 1565 return true;
0aabccc0
DD
1566 }
1567
d62a17ae 1568 if (nexthop->type == NEXTHOP_TYPE_IPV4
1569 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) {
1570 /* Send deletes to the kernel without specifying the next-hop */
0be6e7d7
JU
1571 if (cmd != RTM_DELROUTE) {
1572 if (!_netlink_route_add_gateway_info(
1573 rtmsg->rtm_family, AF_INET, nlmsg, req_size,
1574 bytelen, nexthop))
1575 return false;
1576 }
d62a17ae 1577
1578 if (cmd == RTM_NEWROUTE) {
a757997c
JU
1579 if (!_netlink_route_encode_nexthop_src(
1580 nexthop, AF_INET, nlmsg, req_size, bytelen))
1581 return false;
d62a17ae 1582 }
1583
9266b315
RZ
1584 if (IS_ZEBRA_DEBUG_KERNEL) {
1585 inet_ntop(AF_INET, &nexthop->gate.ipv4, addrstr,
1586 sizeof(addrstr));
1587 zlog_debug("%s: (%s): %pFX nexthop via %s %s if %u vrf %s(%u)",
1588 __func__, routedesc, p, addrstr, label_buf,
1589 nexthop->ifindex, VRF_LOGNAME(vrf),
1590 nexthop->vrf_id);
1591 }
0aabccc0 1592 }
fa713d9e 1593
d62a17ae 1594 if (nexthop->type == NEXTHOP_TYPE_IPV6
1595 || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) {
0be6e7d7
JU
1596 if (!_netlink_route_add_gateway_info(rtmsg->rtm_family,
1597 AF_INET6, nlmsg, req_size,
1598 bytelen, nexthop))
1599 return false;
d62a17ae 1600
1601 if (cmd == RTM_NEWROUTE) {
a757997c
JU
1602 if (!_netlink_route_encode_nexthop_src(
1603 nexthop, AF_INET6, nlmsg, req_size,
1604 bytelen))
1605 return false;
d62a17ae 1606 }
fa713d9e 1607
9266b315
RZ
1608 if (IS_ZEBRA_DEBUG_KERNEL) {
1609 inet_ntop(AF_INET6, &nexthop->gate.ipv6, addrstr,
1610 sizeof(addrstr));
1611 zlog_debug("%s: (%s): %pFX nexthop via %s %s if %u vrf %s(%u)",
1612 __func__, routedesc, p, addrstr, label_buf,
1613 nexthop->ifindex, VRF_LOGNAME(vrf),
1614 nexthop->vrf_id);
1615 }
d62a17ae 1616 }
5e210522
DS
1617
1618 /*
1619 * We have the ifindex so we should always send it
1620 * This is especially useful if we are doing route
1621 * leaking.
1622 */
0be6e7d7
JU
1623 if (nexthop->type != NEXTHOP_TYPE_BLACKHOLE) {
1624 if (!nl_attr_put32(nlmsg, req_size, RTA_OIF, nexthop->ifindex))
1625 return false;
1626 }
d62a17ae 1627
275565fb 1628 if (nexthop->type == NEXTHOP_TYPE_IFINDEX) {
d62a17ae 1629 if (cmd == RTM_NEWROUTE) {
a757997c
JU
1630 if (!_netlink_route_encode_nexthop_src(
1631 nexthop, AF_INET, nlmsg, req_size, bytelen))
1632 return false;
d62a17ae 1633 }
fa713d9e 1634
d62a17ae 1635 if (IS_ZEBRA_DEBUG_KERNEL)
9266b315
RZ
1636 zlog_debug("%s: (%s): %pFX nexthop via if %u vrf %s(%u)",
1637 __func__, routedesc, p, nexthop->ifindex,
1638 VRF_LOGNAME(vrf), nexthop->vrf_id);
0aabccc0 1639 }
0be6e7d7
JU
1640
1641 return true;
fa713d9e
CF
1642}
1643
63eaefa8
R
1644/* This function appends tag value as rtnl flow attribute
1645 * to the given netlink msg only if value is less than 256.
1646 * Used only if SUPPORT_REALMS enabled.
1647 *
1648 * @param nlmsg: nlmsghdr structure to fill in.
1649 * @param maxlen: The size allocated for the message.
1650 * @param tag: The route tag.
1651 *
1652 * The function returns true if the flow attribute could
1653 * be added to the message, otherwise false is returned.
1654 */
1655static inline bool _netlink_set_tag(struct nlmsghdr *n, unsigned int maxlen,
1656 route_tag_t tag)
1657{
1658 if (tag > 0 && tag <= 255) {
1659 if (!nl_attr_put32(n, maxlen, RTA_FLOW, tag))
1660 return false;
1661 }
1662 return true;
1663}
1664
fa713d9e 1665/* This function takes a nexthop as argument and
312a6bee 1666 * appends to the given netlink msg. If the nexthop
fa713d9e
CF
1667 * defines a preferred source, the src parameter
1668 * will be modified to point to that src, otherwise
1669 * it will be kept unmodified.
1670 *
1671 * @param routedesc: Human readable description of route type
1672 * (direct/recursive, single-/multipath)
1673 * @param bytelen: Length of addresses in bytes.
1674 * @param nexthop: Nexthop information
312a6bee
JU
1675 * @param nlmsg: nlmsghdr structure to fill in.
1676 * @param req_size: The size allocated for the message.
fa713d9e
CF
1677 * @param src: pointer pointing to a location where
1678 * the prefsrc should be stored.
0be6e7d7
JU
1679 *
1680 * The function returns true if the nexthop could be added
1681 * to the message, otherwise false is returned.
fa713d9e 1682 */
63eaefa8
R
1683static bool _netlink_route_build_multipath(
1684 const struct prefix *p, const char *routedesc, int bytelen,
1685 const struct nexthop *nexthop, struct nlmsghdr *nlmsg, size_t req_size,
1686 struct rtmsg *rtmsg, const union g_addr **src, route_tag_t tag)
fa713d9e 1687{
9a62e84b 1688 char label_buf[256];
bd47f3a3 1689 struct vrf *vrf;
312a6bee 1690 struct rtnexthop *rtnh;
d62a17ae 1691
312a6bee 1692 rtnh = nl_attr_rtnh(nlmsg, req_size);
0be6e7d7
JU
1693 if (rtnh == NULL)
1694 return false;
d62a17ae 1695
b7537db6
SW
1696 assert(nexthop);
1697
bd47f3a3
JU
1698 vrf = vrf_lookup_by_id(nexthop->vrf_id);
1699
a757997c
JU
1700 if (!_netlink_route_encode_label_info(nexthop->nh_label, nlmsg,
1701 req_size, rtmsg, label_buf,
1702 sizeof(label_buf)))
1703 return false;
d62a17ae 1704
1705 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK))
1706 rtnh->rtnh_flags |= RTNH_F_ONLINK;
1707
002e5c43 1708 if (is_route_v4_over_v6(rtmsg->rtm_family, nexthop->type)) {
d62a17ae 1709 rtnh->rtnh_flags |= RTNH_F_ONLINK;
a757997c 1710 if (!nl_attr_put(nlmsg, req_size, RTA_GATEWAY, &ipv4_ll, 4))
0be6e7d7 1711 return false;
d62a17ae 1712 rtnh->rtnh_ifindex = nexthop->ifindex;
8d27e1aa 1713 if (nexthop->weight)
1714 rtnh->rtnh_hops = nexthop->weight - 1;
d62a17ae 1715
975a328e 1716 if (nexthop->rmap_src.ipv4.s_addr != INADDR_ANY)
d62a17ae 1717 *src = &nexthop->rmap_src;
975a328e 1718 else if (nexthop->src.ipv4.s_addr != INADDR_ANY)
d62a17ae 1719 *src = &nexthop->src;
1720
1721 if (IS_ZEBRA_DEBUG_KERNEL)
1722 zlog_debug(
9266b315
RZ
1723 "%s: 5549 (%s): %pFX nexthop via %s %s if %u vrf %s(%u)",
1724 __func__, routedesc, p, ipv4_ll_buf, label_buf,
bd47f3a3
JU
1725 nexthop->ifindex, VRF_LOGNAME(vrf),
1726 nexthop->vrf_id);
312a6bee 1727 nl_attr_rtnh_end(nlmsg, rtnh);
0be6e7d7 1728 return true;
d62a17ae 1729 }
1730
1731 if (nexthop->type == NEXTHOP_TYPE_IPV4
1732 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) {
0be6e7d7
JU
1733 if (!_netlink_route_add_gateway_info(rtmsg->rtm_family, AF_INET,
1734 nlmsg, req_size, bytelen,
1735 nexthop))
1736 return false;
1737
975a328e 1738 if (nexthop->rmap_src.ipv4.s_addr != INADDR_ANY)
d62a17ae 1739 *src = &nexthop->rmap_src;
975a328e 1740 else if (nexthop->src.ipv4.s_addr != INADDR_ANY)
d62a17ae 1741 *src = &nexthop->src;
1742
a50404aa
RZ
1743 if (IS_ZEBRA_DEBUG_KERNEL)
1744 zlog_debug("%s: (%s): %pFX nexthop via %pI4 %s if %u vrf %s(%u)",
1745 __func__, routedesc, p, &nexthop->gate.ipv4,
1746 label_buf, nexthop->ifindex,
1747 VRF_LOGNAME(vrf), nexthop->vrf_id);
d62a17ae 1748 }
1749 if (nexthop->type == NEXTHOP_TYPE_IPV6
1750 || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) {
0be6e7d7
JU
1751 if (!_netlink_route_add_gateway_info(rtmsg->rtm_family,
1752 AF_INET6, nlmsg, req_size,
1753 bytelen, nexthop))
1754 return false;
d62a17ae 1755
1756 if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->rmap_src.ipv6))
1757 *src = &nexthop->rmap_src;
1758 else if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->src.ipv6))
1759 *src = &nexthop->src;
1760
a50404aa
RZ
1761 if (IS_ZEBRA_DEBUG_KERNEL)
1762 zlog_debug("%s: (%s): %pFX nexthop via %pI6 %s if %u vrf %s(%u)",
1763 __func__, routedesc, p, &nexthop->gate.ipv6,
1764 label_buf, nexthop->ifindex,
1765 VRF_LOGNAME(vrf), nexthop->vrf_id);
d62a17ae 1766 }
5e210522
DS
1767
1768 /*
1769 * We have figured out the ifindex so we should always send it
1770 * This is especially useful if we are doing route
1771 * leaking.
1772 */
1773 if (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)
1774 rtnh->rtnh_ifindex = nexthop->ifindex;
1775
d62a17ae 1776 /* ifindex */
275565fb 1777 if (nexthop->type == NEXTHOP_TYPE_IFINDEX) {
975a328e 1778 if (nexthop->rmap_src.ipv4.s_addr != INADDR_ANY)
d62a17ae 1779 *src = &nexthop->rmap_src;
975a328e 1780 else if (nexthop->src.ipv4.s_addr != INADDR_ANY)
d62a17ae 1781 *src = &nexthop->src;
1782
1783 if (IS_ZEBRA_DEBUG_KERNEL)
9266b315
RZ
1784 zlog_debug("%s: (%s): %pFX nexthop via if %u vrf %s(%u)",
1785 __func__, routedesc, p, nexthop->ifindex,
1786 VRF_LOGNAME(vrf), nexthop->vrf_id);
d62a17ae 1787 }
df7fb580
DS
1788
1789 if (nexthop->weight)
1790 rtnh->rtnh_hops = nexthop->weight - 1;
0be6e7d7 1791
63eaefa8
R
1792 if (!_netlink_set_tag(nlmsg, req_size, tag))
1793 return false;
1794
312a6bee 1795 nl_attr_rtnh_end(nlmsg, rtnh);
0be6e7d7 1796 return true;
fa713d9e
CF
1797}
1798
f2595bd5
DS
1799static inline bool
1800_netlink_mpls_build_singlepath(const struct prefix *p, const char *routedesc,
1801 const struct zebra_nhlfe *nhlfe,
1802 struct nlmsghdr *nlmsg, struct rtmsg *rtmsg,
1803 size_t req_size, int cmd)
40c7bdb0 1804{
d62a17ae 1805 int bytelen;
d7c0a89a 1806 uint8_t family;
40c7bdb0 1807
d62a17ae 1808 family = NHLFE_FAMILY(nhlfe);
1809 bytelen = (family == AF_INET ? 4 : 16);
0be6e7d7
JU
1810 return _netlink_route_build_singlepath(p, routedesc, bytelen,
1811 nhlfe->nexthop, nlmsg, rtmsg,
1812 req_size, cmd);
40c7bdb0 1813}
1814
1815
0be6e7d7 1816static inline bool
9a0132a5 1817_netlink_mpls_build_multipath(const struct prefix *p, const char *routedesc,
f2595bd5 1818 const struct zebra_nhlfe *nhlfe,
312a6bee
JU
1819 struct nlmsghdr *nlmsg, size_t req_size,
1820 struct rtmsg *rtmsg, const union g_addr **src)
40c7bdb0 1821{
d62a17ae 1822 int bytelen;
d7c0a89a 1823 uint8_t family;
40c7bdb0 1824
d62a17ae 1825 family = NHLFE_FAMILY(nhlfe);
1826 bytelen = (family == AF_INET ? 4 : 16);
0be6e7d7
JU
1827 return _netlink_route_build_multipath(p, routedesc, bytelen,
1828 nhlfe->nexthop, nlmsg, req_size,
63eaefa8 1829 rtmsg, src, 0);
40c7bdb0 1830}
1831
d7c0a89a 1832static void _netlink_mpls_debug(int cmd, uint32_t label, const char *routedesc)
40c7bdb0 1833{
d62a17ae 1834 if (IS_ZEBRA_DEBUG_KERNEL)
0be6e7d7
JU
1835 zlog_debug("netlink_mpls_multipath_msg_encode() (%s): %s %u/20",
1836 routedesc, nl_msg_type_to_str(cmd), label);
fa713d9e
CF
1837}
1838
05657ec2
PG
1839static int netlink_neigh_update(int cmd, int ifindex, void *addr, char *lla,
1840 int llalen, ns_id_t ns_id, uint8_t family,
1841 bool permanent, uint8_t protocol)
5c610faf 1842{
d62a17ae 1843 struct {
1844 struct nlmsghdr n;
1845 struct ndmsg ndm;
1846 char buf[256];
1847 } req;
5c610faf 1848
5895d33f 1849 struct zebra_ns *zns = zebra_ns_lookup(ns_id);
8f7d9fc0 1850
5605ecfc 1851 memset(&req, 0, sizeof(req));
5c610faf 1852
d62a17ae 1853 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
1854 req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
1855 req.n.nlmsg_type = cmd; // RTM_NEWNEIGH or RTM_DELNEIGH
1856 req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid;
a55ba23f 1857
05657ec2 1858 req.ndm.ndm_family = family;
d62a17ae 1859 req.ndm.ndm_ifindex = ifindex;
1860 req.ndm.ndm_type = RTN_UNICAST;
05657ec2
PG
1861 if (cmd == RTM_NEWNEIGH) {
1862 if (!permanent)
1863 req.ndm.ndm_state = NUD_REACHABLE;
1864 else
1865 req.ndm.ndm_state = NUD_PERMANENT;
1866 } else
1867 req.ndm.ndm_state = NUD_FAILED;
5c610faf 1868
312a6bee
JU
1869 nl_attr_put(&req.n, sizeof(req), NDA_PROTOCOL, &protocol,
1870 sizeof(protocol));
05657ec2 1871 req.ndm.ndm_type = RTN_UNICAST;
df948efc
PG
1872 nl_attr_put(&req.n, sizeof(req), NDA_DST, addr,
1873 family2addrsize(family));
05657ec2
PG
1874 if (lla)
1875 nl_attr_put(&req.n, sizeof(req), NDA_LLADDR, lla, llalen);
5c610faf 1876
d62a17ae 1877 return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
9bfadae8 1878 false);
5c610faf
DS
1879}
1880
762288f5
SW
1881static bool nexthop_set_src(const struct nexthop *nexthop, int family,
1882 union g_addr *src)
1883{
1884 if (family == AF_INET) {
1885 if (nexthop->rmap_src.ipv4.s_addr != INADDR_ANY) {
1886 src->ipv4 = nexthop->rmap_src.ipv4;
1887 return true;
1888 } else if (nexthop->src.ipv4.s_addr != INADDR_ANY) {
1889 src->ipv4 = nexthop->src.ipv4;
1890 return true;
1891 }
1892 } else if (family == AF_INET6) {
1893 if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->rmap_src.ipv6)) {
1894 src->ipv6 = nexthop->rmap_src.ipv6;
1895 return true;
1896 } else if (!IN6_IS_ADDR_UNSPECIFIED(&nexthop->src.ipv6)) {
1897 src->ipv6 = nexthop->src.ipv6;
1898 return true;
1899 }
1900 }
1901
1902 return false;
1903}
1904
0be6e7d7
JU
1905/*
1906 * The function returns true if the attribute could be added
1907 * to the message, otherwise false is returned.
1908 */
1909static int netlink_route_nexthop_encap(struct nlmsghdr *n, size_t nlen,
1910 struct nexthop *nh)
f2a0ba3a
RZ
1911{
1912 struct rtattr *nest;
1913
1914 switch (nh->nh_encap_type) {
1915 case NET_VXLAN:
a757997c 1916 if (!nl_attr_put16(n, nlen, RTA_ENCAP_TYPE, nh->nh_encap_type))
0be6e7d7 1917 return false;
f2a0ba3a 1918
312a6bee 1919 nest = nl_attr_nest(n, nlen, RTA_ENCAP);
0be6e7d7
JU
1920 if (!nest)
1921 return false;
1922
1923 if (!nl_attr_put32(n, nlen, 0 /* VXLAN_VNI */,
1924 nh->nh_encap.vni))
1925 return false;
312a6bee 1926 nl_attr_nest_end(n, nest);
f2a0ba3a
RZ
1927 break;
1928 }
0be6e7d7
JU
1929
1930 return true;
f2a0ba3a
RZ
1931}
1932
7cdb1a84
MS
1933/*
1934 * Routing table change via netlink interface, using a dataplane context object
0be6e7d7
JU
1935 *
1936 * Returns -1 on failure, 0 when the msg doesn't fit entirely in the buffer
1937 * otherwise the number of bytes written to buf.
7cdb1a84 1938 */
0be6e7d7
JU
1939ssize_t netlink_route_multipath_msg_encode(int cmd,
1940 struct zebra_dplane_ctx *ctx,
1941 uint8_t *data, size_t datalen,
1942 bool fpm, bool force_nhg)
7cdb1a84
MS
1943{
1944 int bytelen;
7cdb1a84
MS
1945 struct nexthop *nexthop = NULL;
1946 unsigned int nexthop_num;
7cdb1a84 1947 const char *routedesc;
762288f5 1948 bool setsrc = false;
7cdb1a84
MS
1949 union g_addr src;
1950 const struct prefix *p, *src_p;
1951 uint32_t table_id;
d4000d7b 1952 struct nlsock *nl;
63eaefa8 1953 route_tag_t tag = 0;
7cdb1a84
MS
1954
1955 struct {
1956 struct nlmsghdr n;
1957 struct rtmsg r;
e57a3fab
RZ
1958 char buf[];
1959 } *req = (void *)data;
7cdb1a84
MS
1960
1961 p = dplane_ctx_get_dest(ctx);
1962 src_p = dplane_ctx_get_src(ctx);
1963
0be6e7d7
JU
1964 if (datalen < sizeof(*req))
1965 return 0;
1966
d4000d7b
DS
1967 nl = kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx));
1968
e57a3fab 1969 memset(req, 0, sizeof(*req));
7cdb1a84 1970
b9c87515 1971 bytelen = (p->family == AF_INET ? 4 : 16);
7cdb1a84 1972
e57a3fab
RZ
1973 req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
1974 req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
7cdb1a84 1975
334734a8
DS
1976 if ((cmd == RTM_NEWROUTE) &&
1977 ((p->family == AF_INET) || v6_rr_semantics))
e57a3fab 1978 req->n.nlmsg_flags |= NLM_F_REPLACE;
7cdb1a84 1979
e57a3fab 1980 req->n.nlmsg_type = cmd;
7cdb1a84 1981
d4000d7b 1982 req->n.nlmsg_pid = nl->snl.nl_pid;
7cdb1a84 1983
b9c87515 1984 req->r.rtm_family = p->family;
e57a3fab
RZ
1985 req->r.rtm_dst_len = p->prefixlen;
1986 req->r.rtm_src_len = src_p ? src_p->prefixlen : 0;
1987 req->r.rtm_scope = RT_SCOPE_UNIVERSE;
7cdb1a84 1988
5709131c 1989 if (cmd == RTM_DELROUTE)
e57a3fab 1990 req->r.rtm_protocol = zebra2proto(dplane_ctx_get_old_type(ctx));
5709131c 1991 else
e57a3fab 1992 req->r.rtm_protocol = zebra2proto(dplane_ctx_get_type(ctx));
7cdb1a84
MS
1993
1994 /*
1995 * blackhole routes are not RTN_UNICAST, they are
1996 * RTN_ BLACKHOLE|UNREACHABLE|PROHIBIT
1997 * so setting this value as a RTN_UNICAST would
1998 * cause the route lookup of just the prefix
1999 * to fail. So no need to specify this for
2000 * the RTM_DELROUTE case
2001 */
2002 if (cmd != RTM_DELROUTE)
e57a3fab 2003 req->r.rtm_type = RTN_UNICAST;
7cdb1a84 2004
0be6e7d7
JU
2005 if (!nl_attr_put(&req->n, datalen, RTA_DST, &p->u.prefix, bytelen))
2006 return 0;
2007 if (src_p) {
2008 if (!nl_attr_put(&req->n, datalen, RTA_SRC, &src_p->u.prefix,
2009 bytelen))
2010 return 0;
2011 }
7cdb1a84
MS
2012
2013 /* Metric. */
2014 /* Hardcode the metric for all routes coming from zebra. Metric isn't
2015 * used
2016 * either by the kernel or by zebra. Its purely for calculating best
2017 * path(s)
2018 * by the routing protocol and for communicating with protocol peers.
2019 */
0be6e7d7
JU
2020 if (!nl_attr_put32(&req->n, datalen, RTA_PRIORITY,
2021 NL_DEFAULT_ROUTE_METRIC))
2022 return 0;
7cdb1a84
MS
2023
2024#if defined(SUPPORT_REALMS)
63eaefa8
R
2025 if (cmd == RTM_DELROUTE)
2026 tag = dplane_ctx_get_old_tag(ctx);
2027 else
2028 tag = dplane_ctx_get_tag(ctx);
7cdb1a84 2029#endif
63eaefa8 2030
7cdb1a84
MS
2031 /* Table corresponding to this route. */
2032 table_id = dplane_ctx_get_table(ctx);
2033 if (table_id < 256)
e57a3fab 2034 req->r.rtm_table = table_id;
7cdb1a84 2035 else {
e57a3fab 2036 req->r.rtm_table = RT_TABLE_UNSPEC;
0be6e7d7
JU
2037 if (!nl_attr_put32(&req->n, datalen, RTA_TABLE, table_id))
2038 return 0;
7cdb1a84
MS
2039 }
2040
9266b315
RZ
2041 if (IS_ZEBRA_DEBUG_KERNEL)
2042 zlog_debug(
2043 "%s: %s %pFX vrf %u(%u)", __func__,
2044 nl_msg_type_to_str(cmd), p, dplane_ctx_get_vrf(ctx),
2045 table_id);
7cdb1a84
MS
2046
2047 /*
2048 * If we are not updating the route and we have received
2049 * a route delete, then all we need to fill in is the
2050 * prefix information to tell the kernel to schwack
2051 * it.
2052 */
63eaefa8
R
2053 if (cmd == RTM_DELROUTE) {
2054 if (!_netlink_set_tag(&req->n, datalen, tag))
2055 return 0;
0be6e7d7 2056 return NLMSG_ALIGN(req->n.nlmsg_len);
63eaefa8 2057 }
7cdb1a84
MS
2058
2059 if (dplane_ctx_get_mtu(ctx) || dplane_ctx_get_nh_mtu(ctx)) {
312a6bee 2060 struct rtattr *nest;
7cdb1a84
MS
2061 uint32_t mtu = dplane_ctx_get_mtu(ctx);
2062 uint32_t nexthop_mtu = dplane_ctx_get_nh_mtu(ctx);
5709131c 2063
7cdb1a84
MS
2064 if (!mtu || (nexthop_mtu && nexthop_mtu < mtu))
2065 mtu = nexthop_mtu;
312a6bee
JU
2066
2067 nest = nl_attr_nest(&req->n, datalen, RTA_METRICS);
0be6e7d7
JU
2068 if (nest == NULL)
2069 return 0;
2070
2071 if (!nl_attr_put(&req->n, datalen, RTAX_MTU, &mtu, sizeof(mtu)))
2072 return 0;
312a6bee 2073 nl_attr_nest_end(&req->n, nest);
7cdb1a84
MS
2074 }
2075
4be03ff4
IR
2076 /*
2077 * Always install blackhole routes without using nexthops, because of
2078 * the following kernel problems:
2079 * 1. Kernel nexthops don't suport unreachable/prohibit route types.
2080 * 2. Blackhole kernel nexthops are deleted when loopback is down.
2081 */
2082 nexthop = dplane_ctx_get_ng(ctx)->nexthop;
2083 if (nexthop) {
2084 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
2085 nexthop = nexthop->resolved;
2086
2087 if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) {
2088 switch (nexthop->bh_type) {
2089 case BLACKHOLE_ADMINPROHIB:
2090 req->r.rtm_type = RTN_PROHIBIT;
2091 break;
2092 case BLACKHOLE_REJECT:
2093 req->r.rtm_type = RTN_UNREACHABLE;
2094 break;
2095 default:
2096 req->r.rtm_type = RTN_BLACKHOLE;
2097 break;
2098 }
2099 return NLMSG_ALIGN(req->n.nlmsg_len);
2100 }
2101 }
2102
6c67f41f
SW
2103 if ((!fpm && kernel_nexthops_supported()
2104 && (!proto_nexthops_only()
2105 || is_proto_nhg(dplane_ctx_get_nhe_id(ctx), 0)))
2106 || (fpm && force_nhg)) {
d8bfd8dc 2107 /* Kernel supports nexthop objects */
9a0132a5 2108 if (IS_ZEBRA_DEBUG_KERNEL)
0be6e7d7
JU
2109 zlog_debug("%s: %pFX nhg_id is %u", __func__, p,
2110 dplane_ctx_get_nhe_id(ctx));
e57a3fab 2111
0be6e7d7
JU
2112 if (!nl_attr_put32(&req->n, datalen, RTA_NH_ID,
2113 dplane_ctx_get_nhe_id(ctx)))
2114 return 0;
d8bfd8dc
SW
2115
2116 /* Have to determine src still */
2117 for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) {
2118 if (setsrc)
2119 break;
2120
e57a3fab 2121 setsrc = nexthop_set_src(nexthop, p->family, &src);
d8bfd8dc
SW
2122 }
2123
2124 if (setsrc) {
0be6e7d7
JU
2125 if (p->family == AF_INET) {
2126 if (!nl_attr_put(&req->n, datalen, RTA_PREFSRC,
2127 &src.ipv4, bytelen))
2128 return 0;
2129 } else if (p->family == AF_INET6) {
2130 if (!nl_attr_put(&req->n, datalen, RTA_PREFSRC,
2131 &src.ipv6, bytelen))
2132 return 0;
2133 }
d8bfd8dc 2134 }
f78fe8f3 2135
0be6e7d7 2136 return NLMSG_ALIGN(req->n.nlmsg_len);
de3f5488
SW
2137 }
2138
7cdb1a84 2139 /* Count overall nexthops so we can decide whether to use singlepath
5709131c
MS
2140 * or multipath case.
2141 */
7cdb1a84
MS
2142 nexthop_num = 0;
2143 for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) {
2144 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
2145 continue;
b9c87515 2146 if (!NEXTHOP_IS_ACTIVE(nexthop->flags))
7cdb1a84
MS
2147 continue;
2148
2149 nexthop_num++;
2150 }
2151
2152 /* Singlepath case. */
220f0f42 2153 if (nexthop_num == 1) {
7cdb1a84
MS
2154 nexthop_num = 0;
2155 for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) {
7cdb1a84
MS
2156 if (CHECK_FLAG(nexthop->flags,
2157 NEXTHOP_FLAG_RECURSIVE)) {
5709131c
MS
2158
2159 if (setsrc)
2160 continue;
2161
b9c87515
RZ
2162 setsrc = nexthop_set_src(nexthop, p->family,
2163 &src);
f183e380 2164 continue;
7cdb1a84
MS
2165 }
2166
b9c87515 2167 if (NEXTHOP_IS_ACTIVE(nexthop->flags)) {
7cdb1a84
MS
2168 routedesc = nexthop->rparent
2169 ? "recursive, single-path"
2170 : "single-path";
2171
63eaefa8
R
2172 if (!_netlink_set_tag(&req->n, datalen, tag))
2173 return 0;
2174
0be6e7d7
JU
2175 if (!_netlink_route_build_singlepath(
2176 p, routedesc, bytelen, nexthop,
2177 &req->n, &req->r, datalen, cmd))
2178 return 0;
7cdb1a84
MS
2179 nexthop_num++;
2180 break;
2181 }
f2a0ba3a
RZ
2182
2183 /*
2184 * Add encapsulation information when installing via
2185 * FPM.
2186 */
0be6e7d7
JU
2187 if (fpm) {
2188 if (!netlink_route_nexthop_encap(
2189 &req->n, datalen, nexthop))
2190 return 0;
2191 }
7cdb1a84 2192 }
f2a0ba3a 2193
13e0321a 2194 if (setsrc) {
0be6e7d7
JU
2195 if (p->family == AF_INET) {
2196 if (!nl_attr_put(&req->n, datalen, RTA_PREFSRC,
2197 &src.ipv4, bytelen))
2198 return 0;
2199 } else if (p->family == AF_INET6) {
2200 if (!nl_attr_put(&req->n, datalen, RTA_PREFSRC,
2201 &src.ipv6, bytelen))
2202 return 0;
2203 }
7cdb1a84
MS
2204 }
2205 } else { /* Multipath case */
312a6bee 2206 struct rtattr *nest;
81793ac1 2207 const union g_addr *src1 = NULL;
7cdb1a84 2208
312a6bee 2209 nest = nl_attr_nest(&req->n, datalen, RTA_MULTIPATH);
0be6e7d7
JU
2210 if (nest == NULL)
2211 return 0;
7cdb1a84
MS
2212
2213 nexthop_num = 0;
2214 for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) {
7cdb1a84
MS
2215 if (CHECK_FLAG(nexthop->flags,
2216 NEXTHOP_FLAG_RECURSIVE)) {
2217 /* This only works for IPv4 now */
5709131c
MS
2218 if (setsrc)
2219 continue;
2220
b9c87515
RZ
2221 setsrc = nexthop_set_src(nexthop, p->family,
2222 &src);
78e54ded 2223 continue;
7cdb1a84
MS
2224 }
2225
b9c87515 2226 if (NEXTHOP_IS_ACTIVE(nexthop->flags)) {
7cdb1a84
MS
2227 routedesc = nexthop->rparent
2228 ? "recursive, multipath"
2229 : "multipath";
2230 nexthop_num++;
2231
0be6e7d7
JU
2232 if (!_netlink_route_build_multipath(
2233 p, routedesc, bytelen, nexthop,
63eaefa8
R
2234 &req->n, datalen, &req->r, &src1,
2235 tag))
0be6e7d7 2236 return 0;
7cdb1a84
MS
2237
2238 if (!setsrc && src1) {
b9c87515 2239 if (p->family == AF_INET)
7cdb1a84 2240 src.ipv4 = src1->ipv4;
b9c87515 2241 else if (p->family == AF_INET6)
7cdb1a84
MS
2242 src.ipv6 = src1->ipv6;
2243
2244 setsrc = 1;
2245 }
2246 }
312a6bee 2247 }
0be6e7d7 2248
312a6bee
JU
2249 nl_attr_nest_end(&req->n, nest);
2250
2251 /*
2252 * Add encapsulation information when installing via
2253 * FPM.
2254 */
2255 if (fpm) {
2256 for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx),
2257 nexthop)) {
2258 if (CHECK_FLAG(nexthop->flags,
2259 NEXTHOP_FLAG_RECURSIVE))
2260 continue;
0be6e7d7
JU
2261 if (!netlink_route_nexthop_encap(
2262 &req->n, datalen, nexthop))
2263 return 0;
312a6bee 2264 }
7cdb1a84 2265 }
f2a0ba3a 2266
312a6bee 2267
13e0321a 2268 if (setsrc) {
0be6e7d7
JU
2269 if (p->family == AF_INET) {
2270 if (!nl_attr_put(&req->n, datalen, RTA_PREFSRC,
2271 &src.ipv4, bytelen))
2272 return 0;
2273 } else if (p->family == AF_INET6) {
2274 if (!nl_attr_put(&req->n, datalen, RTA_PREFSRC,
2275 &src.ipv6, bytelen))
2276 return 0;
2277 }
7cdb1a84
MS
2278 if (IS_ZEBRA_DEBUG_KERNEL)
2279 zlog_debug("Setting source");
2280 }
7cdb1a84
MS
2281 }
2282
2283 /* If there is no useful nexthop then return. */
2284 if (nexthop_num == 0) {
2285 if (IS_ZEBRA_DEBUG_KERNEL)
9266b315 2286 zlog_debug("%s: No useful nexthop.", __func__);
7cdb1a84
MS
2287 }
2288
312a6bee 2289 return NLMSG_ALIGN(req->n.nlmsg_len);
7cdb1a84
MS
2290}
2291
43b5cc5e 2292int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in)
e3be0432 2293{
5523c156 2294 uint32_t actual_table;
d62a17ae 2295 int suc = 0;
2296 struct mcast_route_data *mr = (struct mcast_route_data *)in;
bd8b9272
DS
2297 struct {
2298 struct nlmsghdr n;
2299 struct ndmsg ndm;
2300 char buf[256];
2301 } req;
e3be0432 2302
d62a17ae 2303 mroute = mr;
5895d33f 2304 struct zebra_ns *zns;
bd8b9272 2305
009f8ad5 2306 zns = zvrf->zns;
5605ecfc 2307 memset(&req, 0, sizeof(req));
bd8b9272
DS
2308
2309 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
2310 req.n.nlmsg_flags = NLM_F_REQUEST;
2311 req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid;
2312
bd8b9272
DS
2313 req.n.nlmsg_type = RTM_GETROUTE;
2314
a757997c
JU
2315 nl_attr_put32(&req.n, sizeof(req), RTA_IIF, mroute->ifindex);
2316 nl_attr_put32(&req.n, sizeof(req), RTA_OIF, mroute->ifindex);
4d3b4b18
MR
2317
2318 if (mroute->family == AF_INET) {
2319 req.ndm.ndm_family = RTNL_FAMILY_IPMR;
2320 nl_attr_put(&req.n, sizeof(req), RTA_SRC,
2321 &mroute->src.ipaddr_v4,
2322 sizeof(mroute->src.ipaddr_v4));
2323 nl_attr_put(&req.n, sizeof(req), RTA_DST,
2324 &mroute->grp.ipaddr_v4,
2325 sizeof(mroute->grp.ipaddr_v4));
2326 } else {
2327 req.ndm.ndm_family = RTNL_FAMILY_IP6MR;
2328 nl_attr_put(&req.n, sizeof(req), RTA_SRC,
2329 &mroute->src.ipaddr_v6,
2330 sizeof(mroute->src.ipaddr_v6));
2331 nl_attr_put(&req.n, sizeof(req), RTA_DST,
2332 &mroute->grp.ipaddr_v6,
2333 sizeof(mroute->grp.ipaddr_v6));
2334 }
2335
5523c156
DS
2336 /*
2337 * What?
2338 *
2339 * So during the namespace cleanup we started storing
2340 * the zvrf table_id for the default table as RT_TABLE_MAIN
2341 * which is what the normal routing table for ip routing is.
2342 * This change caused this to break our lookups of sg data
2343 * because prior to this change the zvrf->table_id was 0
2344 * and when the pim multicast kernel code saw a 0,
2345 * it was auto-translated to RT_TABLE_DEFAULT. But since
2346 * we are now passing in RT_TABLE_MAIN there is no auto-translation
2347 * and the kernel goes screw you and the delicious cookies you
2348 * are trying to give me. So now we have this little hack.
2349 */
2350 actual_table = (zvrf->table_id == RT_TABLE_MAIN) ? RT_TABLE_DEFAULT :
2351 zvrf->table_id;
a757997c 2352 nl_attr_put32(&req.n, sizeof(req), RTA_TABLE, actual_table);
e3be0432 2353
bd8b9272 2354 suc = netlink_talk(netlink_route_change_read_multicast, &req.n,
9bfadae8 2355 &zns->netlink_cmd, zns, false);
e3be0432 2356
bd8b9272 2357 mroute = NULL;
d62a17ae 2358 return suc;
e3be0432
DS
2359}
2360
8d03bc50
SW
2361/* Char length to debug ID with */
2362#define ID_LENGTH 10
2363
0be6e7d7 2364static bool _netlink_nexthop_build_group(struct nlmsghdr *n, size_t req_size,
8d03bc50 2365 uint32_t id,
e22e8001 2366 const struct nh_grp *z_grp,
0c8215cb 2367 const uint8_t count)
565ce0d3 2368{
565ce0d3 2369 struct nexthop_grp grp[count];
8d03bc50
SW
2370 /* Need space for max group size, "/", and null term */
2371 char buf[(MULTIPATH_NUM * (ID_LENGTH + 1)) + 1];
2372 char buf1[ID_LENGTH + 2];
2373
2374 buf[0] = '\0';
565ce0d3
SW
2375
2376 memset(grp, 0, sizeof(grp));
2377
2378 if (count) {
0c8215cb 2379 for (int i = 0; i < count; i++) {
e22e8001 2380 grp[i].id = z_grp[i].id;
df7fb580 2381 grp[i].weight = z_grp[i].weight - 1;
8d03bc50
SW
2382
2383 if (IS_ZEBRA_DEBUG_KERNEL) {
2384 if (i == 0)
2385 snprintf(buf, sizeof(buf1), "group %u",
2386 grp[i].id);
2387 else {
2388 snprintf(buf1, sizeof(buf1), "/%u",
2389 grp[i].id);
2390 strlcat(buf, buf1, sizeof(buf));
2391 }
2392 }
565ce0d3 2393 }
0be6e7d7
JU
2394 if (!nl_attr_put(n, req_size, NHA_GROUP, grp,
2395 count * sizeof(*grp)))
2396 return false;
565ce0d3 2397 }
8d03bc50
SW
2398
2399 if (IS_ZEBRA_DEBUG_KERNEL)
2400 zlog_debug("%s: ID (%u): %s", __func__, id, buf);
0be6e7d7
JU
2401
2402 return true;
565ce0d3
SW
2403}
2404
f820d025 2405/**
e9a1cd93 2406 * Next hop packet encoding helper function.
f820d025 2407 *
e9a1cd93
RZ
2408 * \param[in] cmd netlink command.
2409 * \param[in] ctx dataplane context (information snapshot).
2410 * \param[out] buf buffer to hold the packet.
2411 * \param[in] buflen amount of buffer bytes.
f820d025 2412 *
0be6e7d7
JU
2413 * \returns -1 on failure, 0 when the msg doesn't fit entirely in the buffer
2414 * otherwise the number of bytes written to buf.
f820d025 2415 */
0be6e7d7
JU
2416ssize_t netlink_nexthop_msg_encode(uint16_t cmd,
2417 const struct zebra_dplane_ctx *ctx,
2418 void *buf, size_t buflen)
f820d025 2419{
f820d025
SW
2420 struct {
2421 struct nlmsghdr n;
2422 struct nhmsg nhm;
e9a1cd93
RZ
2423 char buf[];
2424 } *req = buf;
f820d025 2425
8d03bc50
SW
2426 mpls_lse_t out_lse[MPLS_MAX_LABELS];
2427 char label_buf[256];
2428 int num_labels = 0;
72938edf
SW
2429 uint32_t id = dplane_ctx_get_nhe_id(ctx);
2430 int type = dplane_ctx_get_nhe_type(ctx);
bdd085a8
MS
2431 struct rtattr *nest;
2432 uint16_t encap;
d4000d7b
DS
2433 struct nlsock *nl =
2434 kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx));
72938edf
SW
2435
2436 if (!id) {
2437 flog_err(
2438 EC_ZEBRA_NHG_FIB_UPDATE,
2439 "Failed trying to update a nexthop group in the kernel that does not have an ID");
2440 return -1;
2441 }
81505946 2442
6c67f41f
SW
2443 /*
2444 * Nothing to do if the kernel doesn't support nexthop objects or
2445 * we dont want to install this type of NHG
2446 */
72938edf
SW
2447 if (!kernel_nexthops_supported()) {
2448 if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_NHG)
2449 zlog_debug(
2450 "%s: nhg_id %u (%s): kernel nexthops not supported, ignoring",
2451 __func__, id, zebra_route_string(type));
6c67f41f 2452 return 0;
72938edf
SW
2453 }
2454
2455 if (proto_nexthops_only() && !is_proto_nhg(id, type)) {
2456 if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_NHG)
2457 zlog_debug(
2458 "%s: nhg_id %u (%s): proto-based nexthops only, ignoring",
2459 __func__, id, zebra_route_string(type));
2460 return 0;
2461 }
6c67f41f 2462
8d03bc50
SW
2463 label_buf[0] = '\0';
2464
0be6e7d7
JU
2465 if (buflen < sizeof(*req))
2466 return 0;
2467
2468 memset(req, 0, sizeof(*req));
f820d025 2469
e9a1cd93
RZ
2470 req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg));
2471 req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
9a1588c4
SW
2472
2473 if (cmd == RTM_NEWNEXTHOP)
e9a1cd93 2474 req->n.nlmsg_flags |= NLM_F_REPLACE;
9a1588c4 2475
e9a1cd93 2476 req->n.nlmsg_type = cmd;
d4000d7b 2477 req->n.nlmsg_pid = nl->snl.nl_pid;
f820d025 2478
e9a1cd93 2479 req->nhm.nh_family = AF_UNSPEC;
fec211ad 2480 /* TODO: Scope? */
f820d025 2481
0be6e7d7
JU
2482 if (!nl_attr_put32(&req->n, buflen, NHA_ID, id))
2483 return 0;
f820d025
SW
2484
2485 if (cmd == RTM_NEWNEXTHOP) {
bf1626a6
MS
2486 /*
2487 * We distinguish between a "group", which is a collection
2488 * of ids, and a singleton nexthop with an id. The
2489 * group is installed as an id that just refers to a list of
2490 * other ids.
2491 */
0be6e7d7 2492 if (dplane_ctx_get_nhe_nh_grp_count(ctx)) {
d52c949b 2493 if (!_netlink_nexthop_build_group(
0be6e7d7
JU
2494 &req->n, buflen, id,
2495 dplane_ctx_get_nhe_nh_grp(ctx),
2496 dplane_ctx_get_nhe_nh_grp_count(ctx)))
2497 return 0;
2498 } else {
0c8215cb
SW
2499 const struct nexthop *nh =
2500 dplane_ctx_get_nhe_ng(ctx)->nexthop;
2501 afi_t afi = dplane_ctx_get_nhe_afi(ctx);
e8b0e420 2502
0c8215cb 2503 if (afi == AFI_IP)
e9a1cd93 2504 req->nhm.nh_family = AF_INET;
0c8215cb 2505 else if (afi == AFI_IP6)
e9a1cd93 2506 req->nhm.nh_family = AF_INET6;
f820d025 2507
565ce0d3 2508 switch (nh->type) {
a6e6a6d8 2509 case NEXTHOP_TYPE_IPV4:
565ce0d3 2510 case NEXTHOP_TYPE_IPV4_IFINDEX:
0be6e7d7
JU
2511 if (!nl_attr_put(&req->n, buflen, NHA_GATEWAY,
2512 &nh->gate.ipv4,
2513 IPV4_MAX_BYTELEN))
2514 return 0;
565ce0d3 2515 break;
a6e6a6d8 2516 case NEXTHOP_TYPE_IPV6:
565ce0d3 2517 case NEXTHOP_TYPE_IPV6_IFINDEX:
0be6e7d7
JU
2518 if (!nl_attr_put(&req->n, buflen, NHA_GATEWAY,
2519 &nh->gate.ipv6,
2520 IPV6_MAX_BYTELEN))
2521 return 0;
565ce0d3
SW
2522 break;
2523 case NEXTHOP_TYPE_BLACKHOLE:
0be6e7d7
JU
2524 if (!nl_attr_put(&req->n, buflen, NHA_BLACKHOLE,
2525 NULL, 0))
2526 return 0;
8d03bc50
SW
2527 /* Blackhole shouldn't have anymore attributes
2528 */
2529 goto nexthop_done;
565ce0d3
SW
2530 case NEXTHOP_TYPE_IFINDEX:
2531 /* Don't need anymore info for this */
2532 break;
a6e6a6d8
SW
2533 }
2534
2535 if (!nh->ifindex) {
565ce0d3
SW
2536 flog_err(
2537 EC_ZEBRA_NHG_FIB_UPDATE,
2538 "Context received for kernel nexthop update without an interface");
2539 return -1;
565ce0d3
SW
2540 }
2541
0be6e7d7
JU
2542 if (!nl_attr_put32(&req->n, buflen, NHA_OIF,
2543 nh->ifindex))
2544 return 0;
8d03bc50 2545
62d2ecb2 2546 if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_ONLINK))
e9a1cd93 2547 req->nhm.nh_flags |= RTNH_F_ONLINK;
62d2ecb2 2548
8d03bc50
SW
2549 num_labels =
2550 build_label_stack(nh->nh_label, out_lse,
2551 label_buf, sizeof(label_buf));
2552
2553 if (num_labels) {
2554 /* Set the BoS bit */
2555 out_lse[num_labels - 1] |=
2556 htonl(1 << MPLS_LS_S_SHIFT);
2557
2558 /*
2559 * TODO: MPLS unsupported for now in kernel.
2560 */
e9a1cd93 2561 if (req->nhm.nh_family == AF_MPLS)
8d03bc50 2562 goto nexthop_done;
bdd085a8
MS
2563
2564 encap = LWTUNNEL_ENCAP_MPLS;
2565 if (!nl_attr_put16(&req->n, buflen,
2566 NHA_ENCAP_TYPE, encap))
2567 return 0;
2568 nest = nl_attr_nest(&req->n, buflen, NHA_ENCAP);
2569 if (!nest)
2570 return 0;
2571 if (!nl_attr_put(
2572 &req->n, buflen, MPLS_IPTUNNEL_DST,
2573 &out_lse,
2574 num_labels * sizeof(mpls_lse_t)))
2575 return 0;
2576
2577 nl_attr_nest_end(&req->n, nest);
8d03bc50
SW
2578 }
2579
eab0f8f0
HS
2580 if (nh->nh_srv6) {
2581 if (nh->nh_srv6->seg6local_action !=
2582 ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) {
2583 uint32_t action;
2584 uint16_t encap;
2585 struct rtattr *nest;
2586 const struct seg6local_context *ctx;
2587
2588 req->nhm.nh_family = AF_INET6;
2589 action = nh->nh_srv6->seg6local_action;
2590 ctx = &nh->nh_srv6->seg6local_ctx;
2591 encap = LWTUNNEL_ENCAP_SEG6_LOCAL;
2592 if (!nl_attr_put(&req->n, buflen,
2593 NHA_ENCAP_TYPE,
2594 &encap,
2595 sizeof(uint16_t)))
2596 return 0;
8689b25a 2597
eab0f8f0
HS
2598 nest = nl_attr_nest(&req->n, buflen,
2599 NHA_ENCAP | NLA_F_NESTED);
2600 if (!nest)
2601 return 0;
52026569 2602
eab0f8f0
HS
2603 switch (action) {
2604 case SEG6_LOCAL_ACTION_END:
2605 if (!nl_attr_put32(
2606 &req->n, buflen,
52026569
HS
2607 SEG6_LOCAL_ACTION,
2608 SEG6_LOCAL_ACTION_END))
eab0f8f0
HS
2609 return 0;
2610 break;
2611 case SEG6_LOCAL_ACTION_END_X:
2612 if (!nl_attr_put32(
2613 &req->n, buflen,
52026569
HS
2614 SEG6_LOCAL_ACTION,
2615 SEG6_LOCAL_ACTION_END_X))
eab0f8f0
HS
2616 return 0;
2617 if (!nl_attr_put(
2618 &req->n, buflen,
8689b25a 2619 SEG6_LOCAL_NH6, &ctx->nh6,
52026569 2620 sizeof(struct in6_addr)))
eab0f8f0
HS
2621 return 0;
2622 break;
2623 case SEG6_LOCAL_ACTION_END_T:
2624 if (!nl_attr_put32(
2625 &req->n, buflen,
52026569
HS
2626 SEG6_LOCAL_ACTION,
2627 SEG6_LOCAL_ACTION_END_T))
eab0f8f0
HS
2628 return 0;
2629 if (!nl_attr_put32(
2630 &req->n, buflen,
52026569
HS
2631 SEG6_LOCAL_TABLE,
2632 ctx->table))
eab0f8f0
HS
2633 return 0;
2634 break;
2635 case SEG6_LOCAL_ACTION_END_DX4:
2636 if (!nl_attr_put32(
2637 &req->n, buflen,
52026569
HS
2638 SEG6_LOCAL_ACTION,
2639 SEG6_LOCAL_ACTION_END_DX4))
eab0f8f0
HS
2640 return 0;
2641 if (!nl_attr_put(
2642 &req->n, buflen,
8689b25a 2643 SEG6_LOCAL_NH4, &ctx->nh4,
52026569 2644 sizeof(struct in_addr)))
eab0f8f0
HS
2645 return 0;
2646 break;
2647 case SEG6_LOCAL_ACTION_END_DT6:
2648 if (!nl_attr_put32(
2649 &req->n, buflen,
52026569
HS
2650 SEG6_LOCAL_ACTION,
2651 SEG6_LOCAL_ACTION_END_DT6))
eab0f8f0
HS
2652 return 0;
2653 if (!nl_attr_put32(
2654 &req->n, buflen,
52026569
HS
2655 SEG6_LOCAL_TABLE,
2656 ctx->table))
eab0f8f0
HS
2657 return 0;
2658 break;
7eab60a7
RS
2659 case SEG6_LOCAL_ACTION_END_DT4:
2660 if (!nl_attr_put32(
2661 &req->n, buflen,
2662 SEG6_LOCAL_ACTION,
2663 SEG6_LOCAL_ACTION_END_DT4))
2664 return 0;
2665 if (!nl_attr_put32(
2666 &req->n, buflen,
2667 SEG6_LOCAL_VRFTABLE,
2668 ctx->table))
2669 return 0;
2670 break;
eab0f8f0
HS
2671 default:
2672 zlog_err("%s: unsupport seg6local behaviour action=%u",
2673 __func__, action);
0a543b79 2674 return 0;
eab0f8f0
HS
2675 }
2676 nl_attr_nest_end(&req->n, nest);
8689b25a 2677 }
8689b25a 2678
eab0f8f0
HS
2679 if (!sid_zero(&nh->nh_srv6->seg6_segs)) {
2680 char tun_buf[4096];
2681 ssize_t tun_len;
2682 struct rtattr *nest;
76fb7ae4 2683
eab0f8f0
HS
2684 if (!nl_attr_put16(&req->n, buflen,
2685 NHA_ENCAP_TYPE,
2686 LWTUNNEL_ENCAP_SEG6))
2687 return 0;
2688 nest = nl_attr_nest(&req->n, buflen,
2689 NHA_ENCAP | NLA_F_NESTED);
2690 if (!nest)
2691 return 0;
2692 tun_len = fill_seg6ipt_encap(tun_buf,
2693 sizeof(tun_buf),
2694 &nh->nh_srv6->seg6_segs);
2695 if (tun_len < 0)
2696 return 0;
2697 if (!nl_attr_put(&req->n, buflen,
2698 SEG6_IPTUNNEL_SRH,
2699 tun_buf, tun_len))
2700 return 0;
2701 nl_attr_nest_end(&req->n, nest);
2702 }
76fb7ae4
HS
2703 }
2704
bf1626a6
MS
2705nexthop_done:
2706
2707 if (IS_ZEBRA_DEBUG_KERNEL)
2c77ddee
DS
2708 zlog_debug("%s: ID (%u): %pNHv(%d) vrf %s(%u) %s ",
2709 __func__, id, nh, nh->ifindex,
bd47f3a3
JU
2710 vrf_id_to_name(nh->vrf_id),
2711 nh->vrf_id, label_buf);
bdd085a8 2712 }
f820d025 2713
bdd085a8 2714 req->nhm.nh_protocol = zebra2proto(type);
f820d025 2715
f820d025
SW
2716 } else if (cmd != RTM_DELNEXTHOP) {
2717 flog_err(
2718 EC_ZEBRA_NHG_FIB_UPDATE,
2719 "Nexthop group kernel update command (%d) does not exist",
2720 cmd);
2721 return -1;
2722 }
2723
9266b315
RZ
2724 if (IS_ZEBRA_DEBUG_KERNEL)
2725 zlog_debug("%s: %s, id=%u", __func__, nl_msg_type_to_str(cmd),
2726 id);
f820d025 2727
e9a1cd93 2728 return NLMSG_ALIGN(req->n.nlmsg_len);
f820d025
SW
2729}
2730
67e3369e
JU
2731static ssize_t netlink_nexthop_msg_encoder(struct zebra_dplane_ctx *ctx,
2732 void *buf, size_t buflen)
f820d025 2733{
bf1626a6 2734 enum dplane_op_e op;
98cda54a 2735 int cmd = 0;
f820d025 2736
bf1626a6
MS
2737 op = dplane_ctx_get_op(ctx);
2738 if (op == DPLANE_OP_NH_INSTALL || op == DPLANE_OP_NH_UPDATE)
f820d025 2739 cmd = RTM_NEWNEXTHOP;
bf1626a6
MS
2740 else if (op == DPLANE_OP_NH_DELETE)
2741 cmd = RTM_DELNEXTHOP;
2742 else {
2743 flog_err(EC_ZEBRA_NHG_FIB_UPDATE,
2744 "Context received for kernel nexthop update with incorrect OP code (%u)",
2745 op);
67e3369e 2746 return -1;
f820d025
SW
2747 }
2748
67e3369e
JU
2749 return netlink_nexthop_msg_encode(cmd, ctx, buf, buflen);
2750}
2751
67e3369e
JU
2752enum netlink_msg_status
2753netlink_put_nexthop_update_msg(struct nl_batch *bth,
2754 struct zebra_dplane_ctx *ctx)
2755{
e9a1cd93
RZ
2756 /* Nothing to do if the kernel doesn't support nexthop objects */
2757 if (!kernel_nexthops_supported())
67e3369e 2758 return FRR_NETLINK_SUCCESS;
e9a1cd93 2759
67e3369e
JU
2760 return netlink_batch_add_msg(bth, ctx, netlink_nexthop_msg_encoder,
2761 false);
2762}
f820d025 2763
67e3369e
JU
2764static ssize_t netlink_newroute_msg_encoder(struct zebra_dplane_ctx *ctx,
2765 void *buf, size_t buflen)
2766{
2767 return netlink_route_multipath_msg_encode(RTM_NEWROUTE, ctx, buf,
2768 buflen, false, false);
f820d025
SW
2769}
2770
67e3369e
JU
2771static ssize_t netlink_delroute_msg_encoder(struct zebra_dplane_ctx *ctx,
2772 void *buf, size_t buflen)
2773{
2774 return netlink_route_multipath_msg_encode(RTM_DELROUTE, ctx, buf,
2775 buflen, false, false);
2776}
2777
2778enum netlink_msg_status
2779netlink_put_route_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx)
7cdb1a84 2780{
67e3369e 2781 int cmd;
7cdb1a84
MS
2782 const struct prefix *p = dplane_ctx_get_dest(ctx);
2783
2784 if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) {
2785 cmd = RTM_DELROUTE;
2786 } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_INSTALL) {
2787 cmd = RTM_NEWROUTE;
2788 } else if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_UPDATE) {
2789
2790 if (p->family == AF_INET || v6_rr_semantics) {
2791 /* Single 'replace' operation */
fe5f21af
DS
2792
2793 /*
2794 * With route replace semantics in place
2795 * for v4 routes and the new route is a system
2796 * route we do not install anything.
2797 * The problem here is that the new system
2798 * route should cause us to withdraw from
2799 * the kernel the old non-system route
2800 */
67e3369e
JU
2801 if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx))
2802 && !RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx)))
6b390b3c 2803 return netlink_batch_add_msg(
67e3369e
JU
2804 bth, ctx, netlink_delroute_msg_encoder,
2805 true);
7cdb1a84
MS
2806 } else {
2807 /*
2808 * So v6 route replace semantics are not in
2809 * the kernel at this point as I understand it.
2810 * so let's do a delete then an add.
2811 * In the future once v6 route replace semantics
2812 * are in we can figure out what to do here to
2813 * allow working with old and new kernels.
2814 *
2815 * I'm also intentionally ignoring the failure case
2816 * of the route delete. If that happens yeah we're
2817 * screwed.
2818 */
67e3369e
JU
2819 if (!RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx)))
2820 netlink_batch_add_msg(
2821 bth, ctx, netlink_delroute_msg_encoder,
2822 true);
7cdb1a84
MS
2823 }
2824
67e3369e
JU
2825 cmd = RTM_NEWROUTE;
2826 } else
2827 return FRR_NETLINK_ERROR;
7cdb1a84 2828
67e3369e
JU
2829 if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx)))
2830 return FRR_NETLINK_SUCCESS;
0be6e7d7 2831
67e3369e
JU
2832 return netlink_batch_add_msg(bth, ctx,
2833 cmd == RTM_NEWROUTE
2834 ? netlink_newroute_msg_encoder
2835 : netlink_delroute_msg_encoder,
2836 false);
2837}
7cdb1a84 2838
d9f5b2f5
SW
2839/**
2840 * netlink_nexthop_process_nh() - Parse the gatway/if info from a new nexthop
2841 *
2842 * @tb: Netlink RTA data
2843 * @family: Address family in the nhmsg
8c0a24c1 2844 * @ifp: Interface connected - this should be NULL, we fill it in
d9f5b2f5
SW
2845 * @ns_id: Namspace id
2846 *
2847 * Return: New nexthop
2848 */
e22e8001
SW
2849static struct nexthop netlink_nexthop_process_nh(struct rtattr **tb,
2850 unsigned char family,
2851 struct interface **ifp,
2852 ns_id_t ns_id)
d9f5b2f5 2853{
e22e8001 2854 struct nexthop nh = {};
d9f5b2f5 2855 void *gate = NULL;
8e401b25 2856 enum nexthop_types_t type = 0;
e22e8001
SW
2857 int if_index = 0;
2858 size_t sz = 0;
7134ba70 2859 struct interface *ifp_lookup;
d9f5b2f5
SW
2860
2861 if_index = *(int *)RTA_DATA(tb[NHA_OIF]);
2862
8e401b25 2863
d9f5b2f5
SW
2864 if (tb[NHA_GATEWAY]) {
2865 switch (family) {
2866 case AF_INET:
8e401b25 2867 type = NEXTHOP_TYPE_IPV4_IFINDEX;
d9f5b2f5
SW
2868 sz = 4;
2869 break;
2870 case AF_INET6:
8e401b25 2871 type = NEXTHOP_TYPE_IPV6_IFINDEX;
d9f5b2f5
SW
2872 sz = 16;
2873 break;
2874 default:
2875 flog_warn(
2876 EC_ZEBRA_BAD_NHG_MESSAGE,
c4239c05 2877 "Nexthop gateway with bad address family (%d) received from kernel",
d9f5b2f5 2878 family);
e22e8001 2879 return nh;
d9f5b2f5
SW
2880 }
2881 gate = RTA_DATA(tb[NHA_GATEWAY]);
e22e8001 2882 } else
8e401b25 2883 type = NEXTHOP_TYPE_IFINDEX;
d9f5b2f5 2884
8e401b25 2885 if (type)
e22e8001 2886 nh.type = type;
8e401b25
SW
2887
2888 if (gate)
e22e8001 2889 memcpy(&(nh.gate), gate, sz);
8e401b25
SW
2890
2891 if (if_index)
e22e8001 2892 nh.ifindex = if_index;
8e401b25 2893
7134ba70
DS
2894 ifp_lookup =
2895 if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), nh.ifindex);
2896
e22e8001 2897 if (ifp)
7134ba70
DS
2898 *ifp = ifp_lookup;
2899 if (ifp_lookup)
096f7609 2900 nh.vrf_id = ifp_lookup->vrf->vrf_id;
e22e8001 2901 else {
d9f5b2f5
SW
2902 flog_warn(
2903 EC_ZEBRA_UNKNOWN_INTERFACE,
2904 "%s: Unknown nexthop interface %u received, defaulting to VRF_DEFAULT",
15569c58 2905 __func__, nh.ifindex);
d9f5b2f5 2906
e22e8001 2907 nh.vrf_id = VRF_DEFAULT;
d9f5b2f5
SW
2908 }
2909
2910 if (tb[NHA_ENCAP] && tb[NHA_ENCAP_TYPE]) {
2911 uint16_t encap_type = *(uint16_t *)RTA_DATA(tb[NHA_ENCAP_TYPE]);
2912 int num_labels = 0;
6e728764 2913
d9f5b2f5
SW
2914 mpls_label_t labels[MPLS_MAX_LABELS] = {0};
2915
e22e8001 2916 if (encap_type == LWTUNNEL_ENCAP_MPLS)
d9f5b2f5 2917 num_labels = parse_encap_mpls(tb[NHA_ENCAP], labels);
d9f5b2f5 2918
e22e8001
SW
2919 if (num_labels)
2920 nexthop_add_labels(&nh, ZEBRA_LSP_STATIC, num_labels,
d9f5b2f5 2921 labels);
d9f5b2f5
SW
2922 }
2923
2924 return nh;
2925}
2926
85f5e761 2927static int netlink_nexthop_process_group(struct rtattr **tb,
5a935f79 2928 struct nh_grp *z_grp, int z_grp_size)
d9f5b2f5 2929{
e22e8001
SW
2930 uint8_t count = 0;
2931 /* linux/nexthop.h group struct */
d9f5b2f5
SW
2932 struct nexthop_grp *n_grp = NULL;
2933
85f5e761 2934 n_grp = (struct nexthop_grp *)RTA_DATA(tb[NHA_GROUP]);
d9f5b2f5
SW
2935 count = (RTA_PAYLOAD(tb[NHA_GROUP]) / sizeof(*n_grp));
2936
2937 if (!count || (count * sizeof(*n_grp)) != RTA_PAYLOAD(tb[NHA_GROUP])) {
2938 flog_warn(EC_ZEBRA_BAD_NHG_MESSAGE,
2939 "Invalid nexthop group received from the kernel");
85f5e761 2940 return count;
d9f5b2f5
SW
2941 }
2942
5a935f79 2943 for (int i = 0; ((i < count) && (i < z_grp_size)); i++) {
e22e8001 2944 z_grp[i].id = n_grp[i].id;
df7fb580 2945 z_grp[i].weight = n_grp[i].weight + 1;
85f5e761 2946 }
d9f5b2f5
SW
2947 return count;
2948}
2949
2950/**
2951 * netlink_nexthop_change() - Read in change about nexthops from the kernel
2952 *
2953 * @h: Netlink message header
2954 * @ns_id: Namspace id
2955 * @startup: Are we reading under startup conditions?
2956 *
2957 * Return: Result status
2958 */
2959int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id, int startup)
2960{
2961 int len;
2962 /* nexthop group id */
2963 uint32_t id;
2964 unsigned char family;
38e40db1 2965 int type;
e8b0e420 2966 afi_t afi = AFI_UNSPEC;
946de1b9 2967 vrf_id_t vrf_id = VRF_DEFAULT;
8c0a24c1 2968 struct interface *ifp = NULL;
d9f5b2f5 2969 struct nhmsg *nhm = NULL;
e22e8001
SW
2970 struct nexthop nh = {};
2971 struct nh_grp grp[MULTIPATH_NUM] = {};
85f5e761 2972 /* Count of nexthops in group array */
e22e8001 2973 uint8_t grp_count = 0;
e22e8001 2974 struct rtattr *tb[NHA_MAX + 1] = {};
d9f5b2f5 2975
1d80c209
DS
2976 frrtrace(3, frr_zebra, netlink_nexthop_change, h, ns_id, startup);
2977
d9f5b2f5
SW
2978 nhm = NLMSG_DATA(h);
2979
88cafda7
DS
2980 if (ns_id)
2981 vrf_id = ns_id;
2982
d9f5b2f5
SW
2983 if (startup && h->nlmsg_type != RTM_NEWNEXTHOP)
2984 return 0;
2985
2986 len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct nhmsg));
2987 if (len < 0) {
2988 zlog_warn(
2989 "%s: Message received from netlink is of a broken size %d %zu",
15569c58 2990 __func__, h->nlmsg_len,
d9f5b2f5
SW
2991 (size_t)NLMSG_LENGTH(sizeof(struct nhmsg)));
2992 return -1;
2993 }
2994
6e1e2e8d
DS
2995 netlink_parse_rtattr_flags(tb, NHA_MAX, RTM_NHA(nhm), len,
2996 NLA_F_NESTED);
d9f5b2f5
SW
2997
2998
2999 if (!tb[NHA_ID]) {
3000 flog_warn(
3001 EC_ZEBRA_BAD_NHG_MESSAGE,
3002 "Nexthop group without an ID received from the kernel");
3003 return -1;
3004 }
3005
3006 /* We use the ID key'd nhg table for kernel updates */
3007 id = *((uint32_t *)RTA_DATA(tb[NHA_ID]));
d9f5b2f5 3008
506efd37
AK
3009 if (zebra_evpn_mh_is_fdb_nh(id)) {
3010 /* If this is a L2 NH just ignore it */
3011 if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_EVPN_MH_NH) {
3012 zlog_debug("Ignore kernel update (%u) for fdb-nh 0x%x",
3013 h->nlmsg_type, id);
3014 }
3015 return 0;
3016 }
3017
e8b0e420 3018 family = nhm->nh_family;
e8b0e420
SW
3019 afi = family2afi(family);
3020
38e40db1
SW
3021 type = proto2zebra(nhm->nh_protocol, 0, true);
3022
fdee485a
SW
3023 if (IS_ZEBRA_DEBUG_KERNEL)
3024 zlog_debug("%s ID (%u) %s NS %u",
3025 nl_msg_type_to_str(h->nlmsg_type), id,
3026 nl_family_to_str(family), ns_id);
3027
3028
d9f5b2f5
SW
3029 if (h->nlmsg_type == RTM_NEWNEXTHOP) {
3030 if (tb[NHA_GROUP]) {
3031 /**
3032 * If this is a group message its only going to have
3033 * an array of nexthop IDs associated with it
3034 */
5a935f79
SW
3035 grp_count = netlink_nexthop_process_group(
3036 tb, grp, array_size(grp));
85f5e761
SW
3037 } else {
3038 if (tb[NHA_BLACKHOLE]) {
3039 /**
3040 * This nexthop is just for blackhole-ing
3041 * traffic, it should not have an OIF, GATEWAY,
3042 * or ENCAP
3043 */
e22e8001
SW
3044 nh.type = NEXTHOP_TYPE_BLACKHOLE;
3045 nh.bh_type = BLACKHOLE_UNSPEC;
3046 } else if (tb[NHA_OIF])
85f5e761
SW
3047 /**
3048 * This is a true new nexthop, so we need
3049 * to parse the gateway and device info
3050 */
3051 nh = netlink_nexthop_process_nh(tb, family,
3052 &ifp, ns_id);
e22e8001
SW
3053 else {
3054
8e401b25
SW
3055 flog_warn(
3056 EC_ZEBRA_BAD_NHG_MESSAGE,
3057 "Invalid Nexthop message received from the kernel with ID (%u)",
3058 id);
3059 return -1;
3060 }
e22e8001
SW
3061 SET_FLAG(nh.flags, NEXTHOP_FLAG_ACTIVE);
3062 if (nhm->nh_flags & RTNH_F_ONLINK)
3063 SET_FLAG(nh.flags, NEXTHOP_FLAG_ONLINK);
3064 vrf_id = nh.vrf_id;
d9f5b2f5
SW
3065 }
3066
38e40db1
SW
3067 if (zebra_nhg_kernel_find(id, &nh, grp, grp_count, vrf_id, afi,
3068 type, startup))
e22e8001 3069 return -1;
8e401b25 3070
9a1588c4 3071 } else if (h->nlmsg_type == RTM_DELNEXTHOP)
88cafda7 3072 zebra_nhg_kernel_del(id, vrf_id);
d9f5b2f5 3073
d9f5b2f5
SW
3074 return 0;
3075}
3076
3077/**
3078 * netlink_request_nexthop() - Request nextop information from the kernel
3079 * @zns: Zebra namespace
3080 * @family: AF_* netlink family
3081 * @type: RTM_* route type
3082 *
3083 * Return: Result status
3084 */
3085static int netlink_request_nexthop(struct zebra_ns *zns, int family, int type)
3086{
3087 struct {
3088 struct nlmsghdr n;
3089 struct nhmsg nhm;
3090 } req;
3091
3092 /* Form the request, specifying filter (rtattr) if needed. */
3093 memset(&req, 0, sizeof(req));
3094 req.n.nlmsg_type = type;
3095 req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
3096 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg));
3097 req.nhm.nh_family = family;
3098
fd3f8e52 3099 return netlink_request(&zns->netlink_cmd, &req);
d9f5b2f5
SW
3100}
3101
7d5bb02b 3102
d9f5b2f5
SW
3103/**
3104 * netlink_nexthop_read() - Nexthop read function using netlink interface
3105 *
3106 * @zns: Zebra name space
3107 *
3108 * Return: Result status
3109 * Only called at bootstrap time.
3110 */
3111int netlink_nexthop_read(struct zebra_ns *zns)
3112{
3113 int ret;
3114 struct zebra_dplane_info dp_info;
3115
3116 zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/);
3117
3118 /* Get nexthop objects */
3119 ret = netlink_request_nexthop(zns, AF_UNSPEC, RTM_GETNEXTHOP);
3120 if (ret < 0)
3121 return ret;
3122 ret = netlink_parse_info(netlink_nexthop_change, &zns->netlink_cmd,
9bfadae8 3123 &dp_info, 0, true);
81505946
SW
3124
3125 if (!ret)
3126 /* If we succesfully read in nexthop objects,
3127 * this kernel must support them.
3128 */
3129 supports_nh = true;
7c99d51b
MS
3130 if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_NHG)
3131 zlog_debug("Nexthop objects %ssupported on this kernel",
3132 supports_nh ? "" : "not ");
090ee856
DS
3133
3134 zebra_router_set_supports_nhgs(supports_nh);
81505946 3135
60e0eaee 3136 return ret;
d9f5b2f5
SW
3137}
3138
3139
05657ec2
PG
3140int kernel_neigh_update(int add, int ifindex, void *addr, char *lla, int llalen,
3141 ns_id_t ns_id, uint8_t family, bool permanent)
6b8a5694 3142{
d62a17ae 3143 return netlink_neigh_update(add ? RTM_NEWNEIGH : RTM_DELNEIGH, ifindex,
05657ec2
PG
3144 addr, lla, llalen, ns_id, family, permanent,
3145 RTPROT_ZEBRA);
6b8a5694 3146}
718e3744 3147
340845e2 3148/**
0be6e7d7
JU
3149 * netlink_neigh_update_msg_encode() - Common helper api for encoding
3150 * evpn neighbor update as netlink messages using dataplane context object.
bbd4285b 3151 * Here, a neighbor refers to a bridge forwarding database entry for
3152 * either unicast forwarding or head-end replication or an IP neighbor
3153 * entry.
340845e2
JU
3154 * @ctx: Dataplane context
3155 * @cmd: Netlink command (RTM_NEWNEIGH or RTM_DELNEIGH)
0a27a2fe
PG
3156 * @lla: A pointer to neighbor cache link layer address
3157 * @llalen: Length of the pointer to neighbor cache link layer
3158 * address
340845e2 3159 * @ip: A neighbor cache n/w layer destination address
bbd4285b 3160 * In the case of bridge FDB, this represnts the remote
3161 * VTEP IP.
340845e2
JU
3162 * @replace_obj: Whether NEW request should replace existing object or
3163 * add to the end of the list
3164 * @family: AF_* netlink family
3165 * @type: RTN_* route type
3166 * @flags: NTF_* flags
3167 * @state: NUD_* states
d4d4ec1c
RZ
3168 * @data: data buffer pointer
3169 * @datalen: total amount of data buffer space
0a27a2fe 3170 * @protocol: protocol information
340845e2 3171 *
0be6e7d7
JU
3172 * Return: 0 when the msg doesn't fit entirely in the buffer
3173 * otherwise the number of bytes written to buf.
13d60d35 3174 */
0be6e7d7 3175static ssize_t netlink_neigh_update_msg_encode(
0a27a2fe
PG
3176 const struct zebra_dplane_ctx *ctx, int cmd, const void *lla,
3177 int llalen, const struct ipaddr *ip, bool replace_obj, uint8_t family,
3178 uint8_t type, uint8_t flags, uint16_t state, uint32_t nhg_id, bool nfy,
ccd187cd 3179 uint8_t nfy_flags, bool ext, uint32_t ext_flags, void *data,
0a27a2fe 3180 size_t datalen, uint8_t protocol)
13d60d35 3181{
d62a17ae 3182 struct {
3183 struct nlmsghdr n;
3184 struct ndmsg ndm;
d4d4ec1c
RZ
3185 char buf[];
3186 } *req = data;
340845e2
JU
3187 int ipa_len;
3188 enum dplane_op_e op;
d62a17ae 3189
0be6e7d7
JU
3190 if (datalen < sizeof(*req))
3191 return 0;
45c80fbd 3192 memset(req, 0, sizeof(*req));
d62a17ae 3193
340845e2
JU
3194 op = dplane_ctx_get_op(ctx);
3195
d4d4ec1c
RZ
3196 req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
3197 req->n.nlmsg_flags = NLM_F_REQUEST;
d62a17ae 3198 if (cmd == RTM_NEWNEIGH)
d4d4ec1c 3199 req->n.nlmsg_flags |=
340845e2
JU
3200 NLM_F_CREATE
3201 | (replace_obj ? NLM_F_REPLACE : NLM_F_APPEND);
d4d4ec1c
RZ
3202 req->n.nlmsg_type = cmd;
3203 req->ndm.ndm_family = family;
3204 req->ndm.ndm_type = type;
3205 req->ndm.ndm_state = state;
3206 req->ndm.ndm_flags = flags;
3207 req->ndm.ndm_ifindex = dplane_ctx_get_ifindex(ctx);
d62a17ae 3208
45c80fbd 3209 if (!nl_attr_put(&req->n, datalen, NDA_PROTOCOL, &protocol,
0be6e7d7
JU
3210 sizeof(protocol)))
3211 return 0;
3212
0a27a2fe
PG
3213 if (lla) {
3214 if (!nl_attr_put(&req->n, datalen, NDA_LLADDR, lla, llalen))
0be6e7d7
JU
3215 return 0;
3216 }
13d60d35 3217
f188e68e 3218 if (nfy) {
4bcdb608
NA
3219 struct rtattr *nest;
3220
3221 nest = nl_attr_nest(&req->n, datalen,
3222 NDA_FDB_EXT_ATTRS | NLA_F_NESTED);
3223 if (!nest)
3224 return 0;
3225
3226 if (!nl_attr_put(&req->n, datalen, NFEA_ACTIVITY_NOTIFY,
3227 &nfy_flags, sizeof(nfy_flags)))
f188e68e 3228 return 0;
4bcdb608
NA
3229 if (!nl_attr_put(&req->n, datalen, NFEA_DONT_REFRESH, NULL, 0))
3230 return 0;
3231
3232 nl_attr_nest_end(&req->n, nest);
f188e68e 3233 }
506efd37 3234
4bcdb608 3235
ccd187cd
AK
3236 if (ext) {
3237 if (!nl_attr_put(&req->n, datalen, NDA_EXT_FLAGS, &ext_flags,
3238 sizeof(ext_flags)))
3239 return 0;
3240 }
3241
80e19eb7
AK
3242 if (nhg_id) {
3243 if (!nl_attr_put32(&req->n, datalen, NDA_NH_ID, nhg_id))
3244 return 0;
3245 } else {
3246 ipa_len =
3247 IS_IPADDR_V4(ip) ? IPV4_MAX_BYTELEN : IPV6_MAX_BYTELEN;
3248 if (!nl_attr_put(&req->n, datalen, NDA_DST, &ip->ip.addr,
3249 ipa_len))
3250 return 0;
3251 }
340845e2
JU
3252
3253 if (op == DPLANE_OP_MAC_INSTALL || op == DPLANE_OP_MAC_DELETE) {
3254 vlanid_t vid = dplane_ctx_mac_get_vlan(ctx);
13d60d35 3255
0be6e7d7
JU
3256 if (vid > 0) {
3257 if (!nl_attr_put16(&req->n, datalen, NDA_VLAN, vid))
3258 return 0;
3259 }
13d60d35 3260
0be6e7d7
JU
3261 if (!nl_attr_put32(&req->n, datalen, NDA_MASTER,
3262 dplane_ctx_mac_get_br_ifindex(ctx)))
3263 return 0;
340845e2 3264 }
13d60d35 3265
d4d4ec1c 3266 return NLMSG_ALIGN(req->n.nlmsg_len);
13d60d35 3267}
3268
340845e2
JU
3269/*
3270 * Add remote VTEP to the flood list for this VxLAN interface (VNI). This
3271 * is done by adding an FDB entry with a MAC of 00:00:00:00:00:00.
3272 */
67e3369e
JU
3273static ssize_t
3274netlink_vxlan_flood_update_ctx(const struct zebra_dplane_ctx *ctx, int cmd,
3275 void *buf, size_t buflen)
340845e2
JU
3276{
3277 struct ethaddr dst_mac = {.octet = {0}};
88217099
PG
3278 int proto = RTPROT_ZEBRA;
3279
3280 if (dplane_ctx_get_type(ctx) != 0)
3281 proto = zebra2proto(dplane_ctx_get_type(ctx));
d4d4ec1c 3282
67e3369e 3283 return netlink_neigh_update_msg_encode(
0a27a2fe
PG
3284 ctx, cmd, (const void *)&dst_mac, ETH_ALEN,
3285 dplane_ctx_neigh_get_ipaddr(ctx), false, PF_BRIDGE, 0, NTF_SELF,
3286 (NUD_NOARP | NUD_PERMANENT), 0 /*nhg*/, false /*nfy*/,
3287 0 /*nfy_flags*/, false /*ext*/, 0 /*ext_flags*/, buf, buflen,
88217099 3288 proto);
340845e2
JU
3289}
3290
2232a77c 3291#ifndef NDA_RTA
d62a17ae 3292#define NDA_RTA(r) \
3293 ((struct rtattr *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
2232a77c 3294#endif
3295
2414abd3 3296static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
2232a77c 3297{
d62a17ae 3298 struct ndmsg *ndm;
3299 struct interface *ifp;
3300 struct zebra_if *zif;
d62a17ae 3301 struct rtattr *tb[NDA_MAX + 1];
3302 struct interface *br_if;
3303 struct ethaddr mac;
3304 vlanid_t vid = 0;
4b3f26f4 3305 struct in_addr vtep_ip;
d62a17ae 3306 int vid_present = 0, dst_present = 0;
d62a17ae 3307 char vid_buf[20];
3308 char dst_buf[30];
a37f4598 3309 bool sticky;
f188e68e
AK
3310 bool local_inactive = false;
3311 bool dp_static = false;
3312 uint32_t nhg_id = 0;
d62a17ae 3313
3314 ndm = NLMSG_DATA(h);
3315
2853fed6 3316 /* We only process macfdb notifications if EVPN is enabled */
3317 if (!is_evpn_enabled())
3318 return 0;
3319
4b3f26f4 3320 /* Parse attributes and extract fields of interest. Do basic
3321 * validation of the fields.
3322 */
4bcdb608
NA
3323 netlink_parse_rtattr_flags(tb, NDA_MAX, NDA_RTA(ndm), len,
3324 NLA_F_NESTED);
d62a17ae 3325
3326 if (!tb[NDA_LLADDR]) {
28bd0652 3327 if (IS_ZEBRA_DEBUG_KERNEL)
4b3f26f4 3328 zlog_debug("%s AF_BRIDGE IF %u - no LLADDR",
28bd0652 3329 nl_msg_type_to_str(h->nlmsg_type),
4b3f26f4 3330 ndm->ndm_ifindex);
d62a17ae 3331 return 0;
3332 }
3333
ff8b7eb8 3334 if (RTA_PAYLOAD(tb[NDA_LLADDR]) != ETH_ALEN) {
28bd0652
DS
3335 if (IS_ZEBRA_DEBUG_KERNEL)
3336 zlog_debug(
4b3f26f4 3337 "%s AF_BRIDGE IF %u - LLADDR is not MAC, len %lu",
3338 nl_msg_type_to_str(h->nlmsg_type), ndm->ndm_ifindex,
28bd0652 3339 (unsigned long)RTA_PAYLOAD(tb[NDA_LLADDR]));
d62a17ae 3340 return 0;
3341 }
3342
ff8b7eb8 3343 memcpy(&mac, RTA_DATA(tb[NDA_LLADDR]), ETH_ALEN);
d62a17ae 3344
2be18df4 3345 if (tb[NDA_VLAN]) {
d62a17ae 3346 vid_present = 1;
d7c0a89a 3347 vid = *(uint16_t *)RTA_DATA(tb[NDA_VLAN]);
772270f3 3348 snprintf(vid_buf, sizeof(vid_buf), " VLAN %u", vid);
d62a17ae 3349 }
3350
3351 if (tb[NDA_DST]) {
3352 /* TODO: Only IPv4 supported now. */
3353 dst_present = 1;
4b3f26f4 3354 memcpy(&vtep_ip.s_addr, RTA_DATA(tb[NDA_DST]),
d62a17ae 3355 IPV4_MAX_BYTELEN);
9bcef951
MS
3356 snprintfrr(dst_buf, sizeof(dst_buf), " dst %pI4",
3357 &vtep_ip);
d62a17ae 3358 }
3359
f188e68e
AK
3360 if (tb[NDA_NH_ID])
3361 nhg_id = *(uint32_t *)RTA_DATA(tb[NDA_NH_ID]);
3362
3363 if (ndm->ndm_state & NUD_STALE)
3364 local_inactive = true;
3365
4bcdb608
NA
3366 if (tb[NDA_FDB_EXT_ATTRS]) {
3367 struct rtattr *attr = tb[NDA_FDB_EXT_ATTRS];
3368 struct rtattr *nfea_tb[NFEA_MAX + 1] = {0};
3369
3370 netlink_parse_rtattr_nested(nfea_tb, NFEA_MAX, attr);
3371 if (nfea_tb[NFEA_ACTIVITY_NOTIFY]) {
3372 uint8_t nfy_flags;
f188e68e 3373
4bcdb608
NA
3374 nfy_flags = *(uint8_t *)RTA_DATA(
3375 nfea_tb[NFEA_ACTIVITY_NOTIFY]);
3376 if (nfy_flags & FDB_NOTIFY_BIT)
3377 dp_static = true;
3378 if (nfy_flags & FDB_NOTIFY_INACTIVE_BIT)
3379 local_inactive = true;
3380 }
f188e68e
AK
3381 }
3382
d62a17ae 3383 if (IS_ZEBRA_DEBUG_KERNEL)
ef7b8be4 3384 zlog_debug("Rx %s AF_BRIDGE IF %u%s st 0x%x fl 0x%x MAC %pEA%s nhg %d",
d62a17ae 3385 nl_msg_type_to_str(h->nlmsg_type),
d62a17ae 3386 ndm->ndm_ifindex, vid_present ? vid_buf : "",
ef7b8be4 3387 ndm->ndm_state, ndm->ndm_flags, &mac,
f188e68e 3388 dst_present ? dst_buf : "", nhg_id);
d62a17ae 3389
4b3f26f4 3390 /* The interface should exist. */
3391 ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id),
3392 ndm->ndm_ifindex);
3393 if (!ifp || !ifp->info)
3394 return 0;
3395
3396 /* The interface should be something we're interested in. */
3397 if (!IS_ZEBRA_IF_BRIDGE_SLAVE(ifp))
3398 return 0;
3399
3400 zif = (struct zebra_if *)ifp->info;
3401 if ((br_if = zif->brslave_info.br_if) == NULL) {
3402 if (IS_ZEBRA_DEBUG_KERNEL)
3403 zlog_debug(
3404 "%s AF_BRIDGE IF %s(%u) brIF %u - no bridge master",
3405 nl_msg_type_to_str(h->nlmsg_type), ifp->name,
3406 ndm->ndm_ifindex,
3407 zif->brslave_info.bridge_ifindex);
3408 return 0;
3409 }
3410
f188e68e 3411 sticky = !!(ndm->ndm_flags & NTF_STICKY);
4b3f26f4 3412
28bd0652
DS
3413 if (filter_vlan && vid != filter_vlan) {
3414 if (IS_ZEBRA_DEBUG_KERNEL)
d6951e5e 3415 zlog_debug(" Filtered due to filter vlan: %d",
28bd0652 3416 filter_vlan);
d62a17ae 3417 return 0;
28bd0652 3418 }
d62a17ae 3419
3420 /* If add or update, do accordingly if learnt on a "local" interface; if
3421 * the notification is over VxLAN, this has to be related to
3422 * multi-homing,
3423 * so perform an implicit delete of any local entry (if it exists).
3424 */
3425 if (h->nlmsg_type == RTM_NEWNEIGH) {
4b3f26f4 3426 /* Drop "permanent" entries. */
3427 if (ndm->ndm_state & NUD_PERMANENT) {
3428 if (IS_ZEBRA_DEBUG_KERNEL)
d6951e5e
DL
3429 zlog_debug(
3430 " Dropping entry because of NUD_PERMANENT");
3431 return 0;
4b3f26f4 3432 }
3433
d62a17ae 3434 if (IS_ZEBRA_IF_VXLAN(ifp))
15400f95
AK
3435 return zebra_vxlan_dp_network_mac_add(
3436 ifp, br_if, &mac, vid, nhg_id, sticky,
3437 !!(ndm->ndm_flags & NTF_EXT_LEARNED));
d62a17ae 3438
3439 return zebra_vxlan_local_mac_add_update(ifp, br_if, &mac, vid,
f188e68e 3440 sticky, local_inactive, dp_static);
d62a17ae 3441 }
3442
3443 /* This is a delete notification.
4b3f26f4 3444 * Ignore the notification with IP dest as it may just signify that the
3445 * MAC has moved from remote to local. The exception is the special
3446 * all-zeros MAC that represents the BUM flooding entry; we may have
3447 * to readd it. Otherwise,
d62a17ae 3448 * 1. For a MAC over VxLan, check if it needs to be refreshed(readded)
3449 * 2. For a MAC over "local" interface, delete the mac
3450 * Note: We will get notifications from both bridge driver and VxLAN
3451 * driver.
d62a17ae 3452 */
f188e68e
AK
3453 if (nhg_id)
3454 return 0;
3455
28bd0652 3456 if (dst_present) {
4b3f26f4 3457 u_char zero_mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
3458
3459 if (!memcmp(zero_mac, mac.octet, ETH_ALEN))
3460 return zebra_vxlan_check_readd_vtep(ifp, vtep_ip);
d62a17ae 3461 return 0;
28bd0652 3462 }
d62a17ae 3463
3464 if (IS_ZEBRA_IF_VXLAN(ifp))
15400f95 3465 return zebra_vxlan_dp_network_mac_del(ifp, br_if, &mac, vid);
d62a17ae 3466
3467 return zebra_vxlan_local_mac_del(ifp, br_if, &mac, vid);
2232a77c 3468}
3469
2414abd3 3470static int netlink_macfdb_table(struct nlmsghdr *h, ns_id_t ns_id, int startup)
2232a77c 3471{
d62a17ae 3472 int len;
3473 struct ndmsg *ndm;
2232a77c 3474
d62a17ae 3475 if (h->nlmsg_type != RTM_NEWNEIGH)
3476 return 0;
2232a77c 3477
d62a17ae 3478 /* Length validity. */
3479 len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct ndmsg));
3480 if (len < 0)
3481 return -1;
2232a77c 3482
d62a17ae 3483 /* We are interested only in AF_BRIDGE notifications. */
3484 ndm = NLMSG_DATA(h);
3485 if (ndm->ndm_family != AF_BRIDGE)
3486 return 0;
2232a77c 3487
2414abd3 3488 return netlink_macfdb_change(h, len, ns_id);
2232a77c 3489}
3490
3491/* Request for MAC FDB information from the kernel */
85a75f1e
MS
3492static int netlink_request_macs(struct nlsock *netlink_cmd, int family,
3493 int type, ifindex_t master_ifindex)
2232a77c 3494{
d62a17ae 3495 struct {
3496 struct nlmsghdr n;
3497 struct ifinfomsg ifm;
3498 char buf[256];
3499 } req;
3500
3501 /* Form the request, specifying filter (rtattr) if needed. */
3502 memset(&req, 0, sizeof(req));
3503 req.n.nlmsg_type = type;
718f9b0f 3504 req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
d62a17ae 3505 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
3506 req.ifm.ifi_family = family;
3507 if (master_ifindex)
312a6bee 3508 nl_attr_put32(&req.n, sizeof(req), IFLA_MASTER, master_ifindex);
d62a17ae 3509
fd3f8e52 3510 return netlink_request(netlink_cmd, &req);
2232a77c 3511}
3512
3513/*
3514 * MAC forwarding database read using netlink interface. This is invoked
3515 * at startup.
3516 */
d62a17ae 3517int netlink_macfdb_read(struct zebra_ns *zns)
2232a77c 3518{
d62a17ae 3519 int ret;
85a75f1e
MS
3520 struct zebra_dplane_info dp_info;
3521
3522 zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/);
d62a17ae 3523
3524 /* Get bridge FDB table. */
85a75f1e
MS
3525 ret = netlink_request_macs(&zns->netlink_cmd, AF_BRIDGE, RTM_GETNEIGH,
3526 0);
d62a17ae 3527 if (ret < 0)
3528 return ret;
3529 /* We are reading entire table. */
3530 filter_vlan = 0;
85a75f1e 3531 ret = netlink_parse_info(netlink_macfdb_table, &zns->netlink_cmd,
9bfadae8 3532 &dp_info, 0, true);
d62a17ae 3533
3534 return ret;
2232a77c 3535}
3536
3537/*
3538 * MAC forwarding database read using netlink interface. This is for a
3539 * specific bridge and matching specific access VLAN (if VLAN-aware bridge).
3540 */
d62a17ae 3541int netlink_macfdb_read_for_bridge(struct zebra_ns *zns, struct interface *ifp,
3542 struct interface *br_if)
2232a77c 3543{
d62a17ae 3544 struct zebra_if *br_zif;
3545 struct zebra_if *zif;
3546 struct zebra_l2info_vxlan *vxl;
85a75f1e 3547 struct zebra_dplane_info dp_info;
d62a17ae 3548 int ret = 0;
3549
85a75f1e 3550 zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/);
d62a17ae 3551
3552 /* Save VLAN we're filtering on, if needed. */
3553 br_zif = (struct zebra_if *)br_if->info;
3554 zif = (struct zebra_if *)ifp->info;
3555 vxl = &zif->l2info.vxl;
3556 if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif))
3557 filter_vlan = vxl->access_vlan;
3558
3559 /* Get bridge FDB table for specific bridge - we do the VLAN filtering.
3560 */
85a75f1e 3561 ret = netlink_request_macs(&zns->netlink_cmd, AF_BRIDGE, RTM_GETNEIGH,
d62a17ae 3562 br_if->ifindex);
3563 if (ret < 0)
3564 return ret;
85a75f1e 3565 ret = netlink_parse_info(netlink_macfdb_table, &zns->netlink_cmd,
9bfadae8 3566 &dp_info, 0, false);
d62a17ae 3567
3568 /* Reset VLAN filter. */
3569 filter_vlan = 0;
3570 return ret;
2232a77c 3571}
3572
67fb9374
CS
3573
3574/* Request for MAC FDB for a specific MAC address in VLAN from the kernel */
3575static int netlink_request_specific_mac_in_bridge(struct zebra_ns *zns,
1a3bd37f 3576 int family, int type,
67fb9374 3577 struct interface *br_if,
1a3bd37f 3578 const struct ethaddr *mac,
67fb9374
CS
3579 vlanid_t vid)
3580{
3581 struct {
3582 struct nlmsghdr n;
3583 struct ndmsg ndm;
3584 char buf[256];
3585 } req;
3586 struct zebra_if *br_zif;
67fb9374
CS
3587
3588 memset(&req, 0, sizeof(req));
3589 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
3590 req.n.nlmsg_type = type; /* RTM_GETNEIGH */
3591 req.n.nlmsg_flags = NLM_F_REQUEST;
3592 req.ndm.ndm_family = family; /* AF_BRIDGE */
3593 /* req.ndm.ndm_state = NUD_REACHABLE; */
3594
312a6bee 3595 nl_attr_put(&req.n, sizeof(req), NDA_LLADDR, mac, 6);
67fb9374
CS
3596
3597 br_zif = (struct zebra_if *)br_if->info;
3598 if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif) && vid > 0)
312a6bee 3599 nl_attr_put16(&req.n, sizeof(req), NDA_VLAN, vid);
67fb9374 3600
312a6bee 3601 nl_attr_put32(&req.n, sizeof(req), NDA_MASTER, br_if->ifindex);
67fb9374
CS
3602
3603 if (IS_ZEBRA_DEBUG_KERNEL)
bd47f3a3 3604 zlog_debug(
ef7b8be4 3605 "%s: Tx family %s IF %s(%u) vrf %s(%u) MAC %pEA vid %u",
bd47f3a3 3606 __func__, nl_family_to_str(req.ndm.ndm_family),
096f7609
IR
3607 br_if->name, br_if->ifindex, br_if->vrf->name,
3608 br_if->vrf->vrf_id, mac, vid);
67fb9374 3609
fd3f8e52 3610 return netlink_request(&zns->netlink_cmd, &req);
67fb9374
CS
3611}
3612
3613int netlink_macfdb_read_specific_mac(struct zebra_ns *zns,
3614 struct interface *br_if,
1a3bd37f 3615 const struct ethaddr *mac, vlanid_t vid)
67fb9374
CS
3616{
3617 int ret = 0;
3618 struct zebra_dplane_info dp_info;
3619
3620 zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/);
3621
3622 /* Get bridge FDB table for specific bridge - we do the VLAN filtering.
3623 */
3624 ret = netlink_request_specific_mac_in_bridge(zns, AF_BRIDGE,
3625 RTM_GETNEIGH,
3626 br_if, mac, vid);
3627 if (ret < 0)
3628 return ret;
3629
3630 ret = netlink_parse_info(netlink_macfdb_table, &zns->netlink_cmd,
9bfadae8 3631 &dp_info, 1, false);
67fb9374
CS
3632
3633 return ret;
3634}
036d93c0
MS
3635
3636/*
3637 * Netlink-specific handler for MAC updates using dataplane context object.
3638 */
67e3369e
JU
3639ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, void *data,
3640 size_t datalen)
2232a77c 3641{
340845e2 3642 struct ipaddr vtep_ip;
036d93c0 3643 vlanid_t vid;
d4d4ec1c
RZ
3644 ssize_t total;
3645 int cmd;
340845e2
JU
3646 uint8_t flags;
3647 uint16_t state;
506efd37 3648 uint32_t nhg_id;
f188e68e
AK
3649 uint32_t update_flags;
3650 bool nfy = false;
3651 uint8_t nfy_flags = 0;
88217099
PG
3652 int proto = RTPROT_ZEBRA;
3653
3654 if (dplane_ctx_get_type(ctx) != 0)
3655 proto = zebra2proto(dplane_ctx_get_type(ctx));
d4d4ec1c
RZ
3656
3657 cmd = dplane_ctx_get_op(ctx) == DPLANE_OP_MAC_INSTALL
3658 ? RTM_NEWNEIGH : RTM_DELNEIGH;
036d93c0 3659
f188e68e 3660 flags = NTF_MASTER;
340845e2 3661 state = NUD_REACHABLE;
d62a17ae 3662
f188e68e
AK
3663 update_flags = dplane_ctx_mac_get_update_flags(ctx);
3664 if (update_flags & DPLANE_MAC_REMOTE) {
3665 flags |= NTF_SELF;
60e372e9
AK
3666 if (dplane_ctx_mac_is_sticky(ctx)) {
3667 /* NUD_NOARP prevents the entry from expiring */
3668 state |= NUD_NOARP;
3669 /* sticky the entry from moving */
f188e68e 3670 flags |= NTF_STICKY;
60e372e9 3671 } else {
f188e68e 3672 flags |= NTF_EXT_LEARNED;
60e372e9 3673 }
f188e68e
AK
3674 /* if it was static-local previously we need to clear the
3675 * notify flags on replace with remote
3676 */
3677 if (update_flags & DPLANE_MAC_WAS_STATIC)
3678 nfy = true;
3679 } else {
3680 /* local mac */
3681 if (update_flags & DPLANE_MAC_SET_STATIC) {
4bcdb608 3682 nfy_flags |= FDB_NOTIFY_BIT;
f188e68e
AK
3683 state |= NUD_NOARP;
3684 }
3685
3686 if (update_flags & DPLANE_MAC_SET_INACTIVE)
4bcdb608 3687 nfy_flags |= FDB_NOTIFY_INACTIVE_BIT;
f188e68e
AK
3688
3689 nfy = true;
3690 }
478566d6 3691
506efd37 3692 nhg_id = dplane_ctx_mac_get_nhg_id(ctx);
340845e2
JU
3693 vtep_ip.ipaddr_v4 = *(dplane_ctx_mac_get_vtep_ip(ctx));
3694 SET_IPADDR_V4(&vtep_ip);
d62a17ae 3695
036d93c0 3696 if (IS_ZEBRA_DEBUG_KERNEL) {
478566d6 3697 char vid_buf[20];
506efd37 3698 const struct ethaddr *mac = dplane_ctx_mac_get_addr(ctx);
478566d6 3699
340845e2
JU
3700 vid = dplane_ctx_mac_get_vlan(ctx);
3701 if (vid > 0)
478566d6
MS
3702 snprintf(vid_buf, sizeof(vid_buf), " VLAN %u", vid);
3703 else
3704 vid_buf[0] = '\0';
036d93c0 3705
ccd187cd 3706 zlog_debug(
ef7b8be4 3707 "Tx %s family %s IF %s(%u)%s %sMAC %pEA dst %pIA nhg %u%s%s%s%s%s",
ccd187cd
AK
3708 nl_msg_type_to_str(cmd), nl_family_to_str(AF_BRIDGE),
3709 dplane_ctx_get_ifname(ctx), dplane_ctx_get_ifindex(ctx),
3710 vid_buf, dplane_ctx_mac_is_sticky(ctx) ? "sticky " : "",
ef7b8be4 3711 mac, &vtep_ip, nhg_id,
ccd187cd
AK
3712 (update_flags & DPLANE_MAC_REMOTE) ? " rem" : "",
3713 (update_flags & DPLANE_MAC_WAS_STATIC) ? " clr_sync"
3714 : "",
3715 (update_flags & DPLANE_MAC_SET_STATIC) ? " static" : "",
3716 (update_flags & DPLANE_MAC_SET_INACTIVE) ? " inactive"
3717 : "",
3718 nfy ? " nfy" : "");
036d93c0 3719 }
d62a17ae 3720
0be6e7d7 3721 total = netlink_neigh_update_msg_encode(
0a27a2fe
PG
3722 ctx, cmd, (const void *)dplane_ctx_mac_get_addr(ctx), ETH_ALEN,
3723 &vtep_ip, true, AF_BRIDGE, 0, flags, state, nhg_id, nfy,
3724 nfy_flags, false /*ext*/, 0 /*ext_flags*/, data, datalen,
88217099 3725 proto);
d4d4ec1c
RZ
3726
3727 return total;
2232a77c 3728}
3729
f17b99ed
DS
3730/*
3731 * In the event the kernel deletes ipv4 link-local neighbor entries created for
3732 * 5549 support, re-install them.
3733 */
3734static void netlink_handle_5549(struct ndmsg *ndm, struct zebra_if *zif,
9b036974
DS
3735 struct interface *ifp, struct ipaddr *ip,
3736 bool handle_failed)
f17b99ed
DS
3737{
3738 if (ndm->ndm_family != AF_INET)
3739 return;
3740
3741 if (!zif->v6_2_v4_ll_neigh_entry)
3742 return;
3743
3744 if (ipv4_ll.s_addr != ip->ip._v4_addr.s_addr)
3745 return;
3746
9b036974
DS
3747 if (handle_failed && ndm->ndm_state & NUD_FAILED) {
3748 zlog_info("Neighbor Entry for %s has entered a failed state, not reinstalling",
3749 ifp->name);
3750 return;
3751 }
3752
f17b99ed
DS
3753 if_nbr_ipv6ll_to_ipv4ll_neigh_update(ifp, &zif->v6_2_v4_ll_addr6, true);
3754}
3755
d62a17ae 3756#define NUD_VALID \
3757 (NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE | NUD_PROBE | NUD_STALE \
3758 | NUD_DELAY)
f188e68e
AK
3759#define NUD_LOCAL_ACTIVE \
3760 (NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE)
2232a77c 3761
80f6b5fa
PG
3762static int netlink_nbr_entry_state_to_zclient(int nbr_state)
3763{
3764 /* an exact match is done between
3765 * - netlink neighbor state values: NDM_XXX (see in linux/neighbour.h)
3766 * - zclient neighbor state values: ZEBRA_NEIGH_STATE_XXX
3767 * (see in lib/zclient.h)
3768 */
3769 return nbr_state;
3770}
2414abd3 3771static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id)
2232a77c 3772{
d62a17ae 3773 struct ndmsg *ndm;
3774 struct interface *ifp;
3775 struct zebra_if *zif;
d62a17ae 3776 struct rtattr *tb[NDA_MAX + 1];
3777 struct interface *link_if;
3778 struct ethaddr mac;
3779 struct ipaddr ip;
3780 char buf[ETHER_ADDR_STRLEN];
d62a17ae 3781 int mac_present = 0;
a37f4598 3782 bool is_ext;
3783 bool is_router;
f188e68e 3784 bool local_inactive;
7c0e4dc6
AK
3785 uint32_t ext_flags = 0;
3786 bool dp_static = false;
7723e8d3
PG
3787 int l2_len = 0;
3788 int cmd;
d62a17ae 3789
3790 ndm = NLMSG_DATA(h);
3791
3792 /* The interface should exist. */
5895d33f 3793 ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id),
d62a17ae 3794 ndm->ndm_ifindex);
2853fed6 3795 if (!ifp || !ifp->info)
d62a17ae 3796 return 0;
3797
20089ae2
DS
3798 zif = (struct zebra_if *)ifp->info;
3799
3800 /* Parse attributes and extract fields of interest. */
20089ae2
DS
3801 netlink_parse_rtattr(tb, NDA_MAX, NDA_RTA(ndm), len);
3802
3803 if (!tb[NDA_DST]) {
bd47f3a3 3804 zlog_debug("%s family %s IF %s(%u) vrf %s(%u) - no DST",
9df414fe
QY
3805 nl_msg_type_to_str(h->nlmsg_type),
3806 nl_family_to_str(ndm->ndm_family), ifp->name,
096f7609 3807 ndm->ndm_ifindex, ifp->vrf->name, ifp->vrf->vrf_id);
d62a17ae 3808 return 0;
20089ae2
DS
3809 }
3810
6006b807 3811 memset(&ip, 0, sizeof(ip));
20089ae2
DS
3812 ip.ipa_type = (ndm->ndm_family == AF_INET) ? IPADDR_V4 : IPADDR_V6;
3813 memcpy(&ip.ip.addr, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST]));
3814
f17b99ed
DS
3815 /* if kernel deletes our rfc5549 neighbor entry, re-install it */
3816 if (h->nlmsg_type == RTM_DELNEIGH && (ndm->ndm_state & NUD_PERMANENT)) {
9b036974 3817 netlink_handle_5549(ndm, zif, ifp, &ip, false);
28bd0652
DS
3818 if (IS_ZEBRA_DEBUG_KERNEL)
3819 zlog_debug(
1d5453d6 3820 " Neighbor Entry Received is a 5549 entry, finished");
20089ae2
DS
3821 return 0;
3822 }
d62a17ae 3823
f17b99ed 3824 /* if kernel marks our rfc5549 neighbor entry invalid, re-install it */
9b036974
DS
3825 if (h->nlmsg_type == RTM_NEWNEIGH && !(ndm->ndm_state & NUD_VALID))
3826 netlink_handle_5549(ndm, zif, ifp, &ip, true);
f17b99ed 3827
7723e8d3
PG
3828 /* we send link layer information to client:
3829 * - nlmsg_type = RTM_DELNEIGH|NEWNEIGH|GETNEIGH
3830 * - struct ipaddr ( for DEL and GET)
3831 * - struct ethaddr mac; (for NEW)
3832 */
3833 if (h->nlmsg_type == RTM_NEWNEIGH)
3834 cmd = ZEBRA_NHRP_NEIGH_ADDED;
3835 else if (h->nlmsg_type == RTM_GETNEIGH)
3836 cmd = ZEBRA_NHRP_NEIGH_GET;
3837 else if (h->nlmsg_type == RTM_DELNEIGH)
3838 cmd = ZEBRA_NHRP_NEIGH_REMOVED;
3839 else {
3840 zlog_debug("%s(): unknown nlmsg type %u", __func__,
3841 h->nlmsg_type);
3842 return 0;
3843 }
3844 if (tb[NDA_LLADDR]) {
3845 /* copy LLADDR information */
3846 l2_len = RTA_PAYLOAD(tb[NDA_LLADDR]);
7723e8d3 3847 }
d603c077
PG
3848 if (l2_len == IPV4_MAX_BYTELEN || l2_len == 0) {
3849 union sockunion link_layer_ipv4;
3850
3851 if (l2_len) {
3852 sockunion_family(&link_layer_ipv4) = AF_INET;
3853 memcpy((void *)sockunion_get_addr(&link_layer_ipv4),
b7c21fad 3854 RTA_DATA(tb[NDA_LLADDR]), l2_len);
d603c077
PG
3855 } else
3856 sockunion_family(&link_layer_ipv4) = AF_UNSPEC;
80f6b5fa
PG
3857 zsend_nhrp_neighbor_notify(
3858 cmd, ifp, &ip,
3859 netlink_nbr_entry_state_to_zclient(ndm->ndm_state),
3860 &link_layer_ipv4);
d603c077 3861 }
7723e8d3
PG
3862
3863 if (h->nlmsg_type == RTM_GETNEIGH)
3864 return 0;
3865
d62a17ae 3866 /* The neighbor is present on an SVI. From this, we locate the
3867 * underlying
3868 * bridge because we're only interested in neighbors on a VxLAN bridge.
3869 * The bridge is located based on the nature of the SVI:
3870 * (a) In the case of a VLAN-aware bridge, the SVI is a L3 VLAN
3871 * interface
3872 * and is linked to the bridge
3873 * (b) In the case of a VLAN-unaware bridge, the SVI is the bridge
3819e4ce 3874 * interface
d62a17ae 3875 * itself
3876 */
3877 if (IS_ZEBRA_IF_VLAN(ifp)) {
5895d33f 3878 link_if = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id),
71349e03 3879 zif->link_ifindex);
d62a17ae 3880 if (!link_if)
3881 return 0;
3882 } else if (IS_ZEBRA_IF_BRIDGE(ifp))
3883 link_if = ifp;
28bd0652
DS
3884 else {
3885 if (IS_ZEBRA_DEBUG_KERNEL)
3886 zlog_debug(
1d5453d6 3887 " Neighbor Entry received is not on a VLAN or a BRIDGE, ignoring");
d62a17ae 3888 return 0;
28bd0652 3889 }
d62a17ae 3890
6006b807 3891 memset(&mac, 0, sizeof(mac));
d62a17ae 3892 if (h->nlmsg_type == RTM_NEWNEIGH) {
3893 if (tb[NDA_LLADDR]) {
ff8b7eb8 3894 if (RTA_PAYLOAD(tb[NDA_LLADDR]) != ETH_ALEN) {
28bd0652
DS
3895 if (IS_ZEBRA_DEBUG_KERNEL)
3896 zlog_debug(
bd47f3a3 3897 "%s family %s IF %s(%u) vrf %s(%u) - LLADDR is not MAC, len %lu",
28bd0652
DS
3898 nl_msg_type_to_str(
3899 h->nlmsg_type),
3900 nl_family_to_str(
3901 ndm->ndm_family),
3902 ifp->name, ndm->ndm_ifindex,
096f7609
IR
3903 ifp->vrf->name,
3904 ifp->vrf->vrf_id,
28bd0652
DS
3905 (unsigned long)RTA_PAYLOAD(
3906 tb[NDA_LLADDR]));
d62a17ae 3907 return 0;
3908 }
3909
3910 mac_present = 1;
ff8b7eb8 3911 memcpy(&mac, RTA_DATA(tb[NDA_LLADDR]), ETH_ALEN);
d62a17ae 3912 }
3913
a37f4598 3914 is_ext = !!(ndm->ndm_flags & NTF_EXT_LEARNED);
3915 is_router = !!(ndm->ndm_flags & NTF_ROUTER);
d62a17ae 3916
7c0e4dc6
AK
3917 if (tb[NDA_EXT_FLAGS]) {
3918 ext_flags = *(uint32_t *)RTA_DATA(tb[NDA_EXT_FLAGS]);
3919 if (ext_flags & NTF_E_MH_PEER_SYNC)
3920 dp_static = true;
3921 }
3922
d62a17ae 3923 if (IS_ZEBRA_DEBUG_KERNEL)
3924 zlog_debug(
ef7b8be4 3925 "Rx %s family %s IF %s(%u) vrf %s(%u) IP %pIA MAC %s state 0x%x flags 0x%x ext_flags 0x%x",
d62a17ae 3926 nl_msg_type_to_str(h->nlmsg_type),
3927 nl_family_to_str(ndm->ndm_family), ifp->name,
096f7609
IR
3928 ndm->ndm_ifindex, ifp->vrf->name,
3929 ifp->vrf->vrf_id, &ip,
d62a17ae 3930 mac_present
3931 ? prefix_mac2str(&mac, buf, sizeof(buf))
3932 : "",
7c0e4dc6 3933 ndm->ndm_state, ndm->ndm_flags, ext_flags);
d62a17ae 3934
3935 /* If the neighbor state is valid for use, process as an add or
3936 * update
3937 * else process as a delete. Note that the delete handling may
3938 * result
3939 * in re-adding the neighbor if it is a valid "remote" neighbor.
3940 */
f188e68e 3941 if (ndm->ndm_state & NUD_VALID) {
c7bfd085
AK
3942 if (zebra_evpn_mh_do_adv_reachable_neigh_only())
3943 local_inactive =
3944 !(ndm->ndm_state & NUD_LOCAL_ACTIVE);
3945 else
3946 /* If EVPN-MH is not enabled we treat STALE
3947 * neighbors as locally-active and advertise
3948 * them
3949 */
3950 local_inactive = false;
f188e68e 3951
ee69da27 3952 return zebra_vxlan_handle_kernel_neigh_update(
7c0e4dc6
AK
3953 ifp, link_if, &ip, &mac, ndm->ndm_state, is_ext,
3954 is_router, local_inactive, dp_static);
f188e68e 3955 }
d62a17ae 3956
ee69da27 3957 return zebra_vxlan_handle_kernel_neigh_del(ifp, link_if, &ip);
d62a17ae 3958 }
3959
3960 if (IS_ZEBRA_DEBUG_KERNEL)
ef7b8be4 3961 zlog_debug("Rx %s family %s IF %s(%u) vrf %s(%u) IP %pIA",
d62a17ae 3962 nl_msg_type_to_str(h->nlmsg_type),
3963 nl_family_to_str(ndm->ndm_family), ifp->name,
096f7609 3964 ndm->ndm_ifindex, ifp->vrf->name, ifp->vrf->vrf_id,
ef7b8be4 3965 &ip);
d62a17ae 3966
3967 /* Process the delete - it may result in re-adding the neighbor if it is
3968 * a valid "remote" neighbor.
3969 */
ee69da27 3970 return zebra_vxlan_handle_kernel_neigh_del(ifp, link_if, &ip);
2232a77c 3971}
3972
2414abd3 3973static int netlink_neigh_table(struct nlmsghdr *h, ns_id_t ns_id, int startup)
2232a77c 3974{
d62a17ae 3975 int len;
3976 struct ndmsg *ndm;
2232a77c 3977
d62a17ae 3978 if (h->nlmsg_type != RTM_NEWNEIGH)
3979 return 0;
2232a77c 3980
d62a17ae 3981 /* Length validity. */
3982 len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct ndmsg));
3983 if (len < 0)
3984 return -1;
2232a77c 3985
d62a17ae 3986 /* We are interested only in AF_INET or AF_INET6 notifications. */
3987 ndm = NLMSG_DATA(h);
3988 if (ndm->ndm_family != AF_INET && ndm->ndm_family != AF_INET6)
3989 return 0;
2232a77c 3990
2414abd3 3991 return netlink_neigh_change(h, len);
2232a77c 3992}
3993
3994/* Request for IP neighbor information from the kernel */
85a75f1e
MS
3995static int netlink_request_neigh(struct nlsock *netlink_cmd, int family,
3996 int type, ifindex_t ifindex)
2232a77c 3997{
d62a17ae 3998 struct {
3999 struct nlmsghdr n;
4000 struct ndmsg ndm;
4001 char buf[256];
4002 } req;
4003
4004 /* Form the request, specifying filter (rtattr) if needed. */
4005 memset(&req, 0, sizeof(req));
4006 req.n.nlmsg_type = type;
718f9b0f 4007 req.n.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
d62a17ae 4008 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
4009 req.ndm.ndm_family = family;
4010 if (ifindex)
312a6bee 4011 nl_attr_put32(&req.n, sizeof(req), NDA_IFINDEX, ifindex);
d62a17ae 4012
fd3f8e52 4013 return netlink_request(netlink_cmd, &req);
2232a77c 4014}
4015
4016/*
4017 * IP Neighbor table read using netlink interface. This is invoked
4018 * at startup.
4019 */
d62a17ae 4020int netlink_neigh_read(struct zebra_ns *zns)
2232a77c 4021{
d62a17ae 4022 int ret;
85a75f1e
MS
4023 struct zebra_dplane_info dp_info;
4024
4025 zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/);
2232a77c 4026
d62a17ae 4027 /* Get IP neighbor table. */
85a75f1e
MS
4028 ret = netlink_request_neigh(&zns->netlink_cmd, AF_UNSPEC, RTM_GETNEIGH,
4029 0);
d62a17ae 4030 if (ret < 0)
4031 return ret;
85a75f1e 4032 ret = netlink_parse_info(netlink_neigh_table, &zns->netlink_cmd,
9bfadae8 4033 &dp_info, 0, true);
2232a77c 4034
d62a17ae 4035 return ret;
2232a77c 4036}
4037
4038/*
4039 * IP Neighbor table read using netlink interface. This is for a specific
4040 * VLAN device.
4041 */
d62a17ae 4042int netlink_neigh_read_for_vlan(struct zebra_ns *zns, struct interface *vlan_if)
2232a77c 4043{
d62a17ae 4044 int ret = 0;
85a75f1e
MS
4045 struct zebra_dplane_info dp_info;
4046
4047 zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/);
2232a77c 4048
85a75f1e 4049 ret = netlink_request_neigh(&zns->netlink_cmd, AF_UNSPEC, RTM_GETNEIGH,
d62a17ae 4050 vlan_if->ifindex);
4051 if (ret < 0)
4052 return ret;
85a75f1e 4053 ret = netlink_parse_info(netlink_neigh_table, &zns->netlink_cmd,
9bfadae8 4054 &dp_info, 0, false);
2232a77c 4055
d62a17ae 4056 return ret;
2232a77c 4057}
4058
67fb9374
CS
4059/*
4060 * Request for a specific IP in VLAN (SVI) device from IP Neighbor table,
4061 * read using netlink interface.
4062 */
4063static int netlink_request_specific_neigh_in_vlan(struct zebra_ns *zns,
1a3bd37f
MS
4064 int type,
4065 const struct ipaddr *ip,
67fb9374
CS
4066 ifindex_t ifindex)
4067{
4068 struct {
4069 struct nlmsghdr n;
4070 struct ndmsg ndm;
4071 char buf[256];
4072 } req;
4073 int ipa_len;
4074
4075 /* Form the request, specifying filter (rtattr) if needed. */
4076 memset(&req, 0, sizeof(req));
4077 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
4078 req.n.nlmsg_flags = NLM_F_REQUEST;
4079 req.n.nlmsg_type = type; /* RTM_GETNEIGH */
4080 req.ndm.ndm_ifindex = ifindex;
4081
4082 if (IS_IPADDR_V4(ip)) {
4083 ipa_len = IPV4_MAX_BYTELEN;
4084 req.ndm.ndm_family = AF_INET;
4085
4086 } else {
4087 ipa_len = IPV6_MAX_BYTELEN;
4088 req.ndm.ndm_family = AF_INET6;
4089 }
4090
312a6bee 4091 nl_attr_put(&req.n, sizeof(req), NDA_DST, &ip->ip.addr, ipa_len);
67fb9374 4092
ef7b8be4
DL
4093 if (IS_ZEBRA_DEBUG_KERNEL)
4094 zlog_debug("%s: Tx %s family %s IF %u IP %pIA flags 0x%x",
7c26c121 4095 __func__, nl_msg_type_to_str(type),
ef7b8be4
DL
4096 nl_family_to_str(req.ndm.ndm_family), ifindex, ip,
4097 req.n.nlmsg_flags);
7c26c121 4098
fd3f8e52 4099 return netlink_request(&zns->netlink_cmd, &req);
67fb9374
CS
4100}
4101
1a3bd37f
MS
4102int netlink_neigh_read_specific_ip(const struct ipaddr *ip,
4103 struct interface *vlan_if)
67fb9374
CS
4104{
4105 int ret = 0;
4106 struct zebra_ns *zns;
096f7609 4107 struct zebra_vrf *zvrf = vlan_if->vrf->info;
67fb9374
CS
4108 struct zebra_dplane_info dp_info;
4109
4110 zns = zvrf->zns;
4111
4112 zebra_dplane_info_from_zns(&dp_info, zns, true /*is_cmd*/);
4113
4114 if (IS_ZEBRA_DEBUG_KERNEL)
ef7b8be4
DL
4115 zlog_debug("%s: neigh request IF %s(%u) IP %pIA vrf %s(%u)",
4116 __func__, vlan_if->name, vlan_if->ifindex, ip,
096f7609 4117 vlan_if->vrf->name, vlan_if->vrf->vrf_id);
67fb9374
CS
4118
4119 ret = netlink_request_specific_neigh_in_vlan(zns, RTM_GETNEIGH, ip,
4120 vlan_if->ifindex);
4121 if (ret < 0)
4122 return ret;
4123
4124 ret = netlink_parse_info(netlink_neigh_table, &zns->netlink_cmd,
9bfadae8 4125 &dp_info, 1, false);
67fb9374
CS
4126
4127 return ret;
4128}
4129
2414abd3 4130int netlink_neigh_change(struct nlmsghdr *h, ns_id_t ns_id)
2232a77c 4131{
d62a17ae 4132 int len;
4133 struct ndmsg *ndm;
2232a77c 4134
7723e8d3
PG
4135 if (!(h->nlmsg_type == RTM_NEWNEIGH || h->nlmsg_type == RTM_DELNEIGH
4136 || h->nlmsg_type == RTM_GETNEIGH))
d62a17ae 4137 return 0;
2232a77c 4138
d62a17ae 4139 /* Length validity. */
4140 len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct ndmsg));
9bdf8618 4141 if (len < 0) {
15569c58
DA
4142 zlog_err(
4143 "%s: Message received from netlink is of a broken size %d %zu",
4144 __func__, h->nlmsg_len,
4145 (size_t)NLMSG_LENGTH(sizeof(struct ndmsg)));
d62a17ae 4146 return -1;
9bdf8618 4147 }
2232a77c 4148
d62a17ae 4149 /* Is this a notification for the MAC FDB or IP neighbor table? */
4150 ndm = NLMSG_DATA(h);
4151 if (ndm->ndm_family == AF_BRIDGE)
2414abd3 4152 return netlink_macfdb_change(h, len, ns_id);
2232a77c 4153
d62a17ae 4154 if (ndm->ndm_type != RTN_UNICAST)
4155 return 0;
2232a77c 4156
d62a17ae 4157 if (ndm->ndm_family == AF_INET || ndm->ndm_family == AF_INET6)
2414abd3 4158 return netlink_ipneigh_change(h, len, ns_id);
8a1b681c 4159 else {
9df414fe 4160 flog_warn(
e914ccbe 4161 EC_ZEBRA_UNKNOWN_FAMILY,
87b5d1b0
DS
4162 "Invalid address family: %u received from kernel neighbor change: %s",
4163 ndm->ndm_family, nl_msg_type_to_str(h->nlmsg_type));
8a1b681c
SW
4164 return 0;
4165 }
2232a77c 4166
d62a17ae 4167 return 0;
2232a77c 4168}
4169
931fa60c
MS
4170/*
4171 * Utility neighbor-update function, using info from dplane context.
4172 */
67e3369e
JU
4173static ssize_t netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx,
4174 int cmd, void *buf, size_t buflen)
2232a77c 4175{
931fa60c 4176 const struct ipaddr *ip;
0a27a2fe
PG
4177 const struct ethaddr *mac = NULL;
4178 const struct ipaddr *link_ip = NULL;
4179 const void *link_ptr = NULL;
4180 char buf2[ETHER_ADDR_STRLEN];
4181
4182 int llalen;
931fa60c
MS
4183 uint8_t flags;
4184 uint16_t state;
340845e2 4185 uint8_t family;
ccd187cd
AK
4186 uint32_t update_flags;
4187 uint32_t ext_flags = 0;
4188 bool ext = false;
88217099
PG
4189 int proto = RTPROT_ZEBRA;
4190
4191 if (dplane_ctx_get_type(ctx) != 0)
4192 proto = zebra2proto(dplane_ctx_get_type(ctx));
d62a17ae 4193
931fa60c 4194 ip = dplane_ctx_neigh_get_ipaddr(ctx);
931fa60c 4195
0a27a2fe
PG
4196 if (dplane_ctx_get_op(ctx) == DPLANE_OP_NEIGH_IP_INSTALL
4197 || dplane_ctx_get_op(ctx) == DPLANE_OP_NEIGH_IP_DELETE) {
4198 link_ip = dplane_ctx_neigh_get_link_ip(ctx);
4199 llalen = IPADDRSZ(link_ip);
4200 link_ptr = (const void *)&(link_ip->ip.addr);
4201 ipaddr2str(link_ip, buf2, sizeof(buf2));
4202 } else {
4203 mac = dplane_ctx_neigh_get_mac(ctx);
4204 llalen = ETH_ALEN;
4205 link_ptr = (const void *)mac;
4206 if (is_zero_mac(mac))
4207 mac = NULL;
4208 if (mac)
4209 prefix_mac2str(mac, buf2, sizeof(buf2));
4210 else
4211 snprintf(buf2, sizeof(buf2), "null");
4212 }
ccd187cd 4213 update_flags = dplane_ctx_neigh_get_update_flags(ctx);
931fa60c
MS
4214 flags = neigh_flags_to_netlink(dplane_ctx_neigh_get_flags(ctx));
4215 state = neigh_state_to_netlink(dplane_ctx_neigh_get_state(ctx));
4216
340845e2 4217 family = IS_IPADDR_V4(ip) ? AF_INET : AF_INET6;
d62a17ae 4218
ccd187cd
AK
4219 if (update_flags & DPLANE_NEIGH_REMOTE) {
4220 flags |= NTF_EXT_LEARNED;
4221 /* if it was static-local previously we need to clear the
4222 * ext flags on replace with remote
4223 */
4224 if (update_flags & DPLANE_NEIGH_WAS_STATIC)
4225 ext = true;
0a27a2fe 4226 } else if (!(update_flags & DPLANE_NEIGH_NO_EXTENSION)) {
ccd187cd
AK
4227 ext = true;
4228 /* local neigh */
4229 if (update_flags & DPLANE_NEIGH_SET_STATIC)
4230 ext_flags |= NTF_E_MH_PEER_SYNC;
ccd187cd 4231 }
ef7b8be4 4232 if (IS_ZEBRA_DEBUG_KERNEL)
340845e2 4233 zlog_debug(
0a27a2fe 4234 "Tx %s family %s IF %s(%u) Neigh %pIA %s %s flags 0x%x state 0x%x %sext_flags 0x%x",
340845e2
JU
4235 nl_msg_type_to_str(cmd), nl_family_to_str(family),
4236 dplane_ctx_get_ifname(ctx), dplane_ctx_get_ifindex(ctx),
0a27a2fe
PG
4237 ip, link_ip ? "Link " : "MAC ", buf2, flags, state,
4238 ext ? "ext " : "", ext_flags);
d62a17ae 4239
18f60fe9 4240 return netlink_neigh_update_msg_encode(
0a27a2fe
PG
4241 ctx, cmd, link_ptr, llalen, ip, true, family, RTN_UNICAST,
4242 flags, state, 0 /*nhg*/, false /*nfy*/, 0 /*nfy_flags*/, ext,
88217099 4243 ext_flags, buf, buflen, proto);
2232a77c 4244}
4245
e18747a9
PG
4246static int netlink_neigh_table_update_ctx(const struct zebra_dplane_ctx *ctx,
4247 void *data, size_t datalen)
4248{
4249 struct {
4250 struct nlmsghdr n;
4251 struct ndtmsg ndtm;
4252 char buf[];
4253 } *req = data;
4254 struct rtattr *nest;
4255 uint8_t family;
4256 ifindex_t idx;
4257 uint32_t val;
4258
4259 if (datalen < sizeof(*req))
4260 return 0;
4261 memset(req, 0, sizeof(*req));
4262 family = dplane_ctx_neightable_get_family(ctx);
4263 idx = dplane_ctx_get_ifindex(ctx);
4264
4265 req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndtmsg));
4266 req->n.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE;
4267 req->n.nlmsg_type = RTM_SETNEIGHTBL;
4268 req->ndtm.ndtm_family = family;
4269
4270 nl_attr_put(&req->n, datalen, NDTA_NAME,
4271 family == AF_INET ? "arp_cache" : "ndisc_cache", 10);
4272 nest = nl_attr_nest(&req->n, datalen, NDTA_PARMS);
4273 if (nest == NULL)
4274 return 0;
4275 if (!nl_attr_put(&req->n, datalen, NDTPA_IFINDEX, &idx, sizeof(idx)))
4276 return 0;
4277 val = dplane_ctx_neightable_get_app_probes(ctx);
4278 if (!nl_attr_put(&req->n, datalen, NDTPA_APP_PROBES, &val, sizeof(val)))
4279 return 0;
4280 val = dplane_ctx_neightable_get_mcast_probes(ctx);
4281 if (!nl_attr_put(&req->n, datalen, NDTPA_MCAST_PROBES, &val,
4282 sizeof(val)))
4283 return 0;
4284 val = dplane_ctx_neightable_get_ucast_probes(ctx);
4285 if (!nl_attr_put(&req->n, datalen, NDTPA_UCAST_PROBES, &val,
4286 sizeof(val)))
4287 return 0;
4288 nl_attr_nest_end(&req->n, nest);
4289
4290 return NLMSG_ALIGN(req->n.nlmsg_len);
4291}
4292
67e3369e
JU
4293static ssize_t netlink_neigh_msg_encoder(struct zebra_dplane_ctx *ctx,
4294 void *buf, size_t buflen)
2232a77c 4295{
67e3369e 4296 ssize_t ret;
2232a77c 4297
931fa60c
MS
4298 switch (dplane_ctx_get_op(ctx)) {
4299 case DPLANE_OP_NEIGH_INSTALL:
4300 case DPLANE_OP_NEIGH_UPDATE:
d68e74b4 4301 case DPLANE_OP_NEIGH_DISCOVER:
0a27a2fe 4302 case DPLANE_OP_NEIGH_IP_INSTALL:
67e3369e 4303 ret = netlink_neigh_update_ctx(ctx, RTM_NEWNEIGH, buf, buflen);
931fa60c
MS
4304 break;
4305 case DPLANE_OP_NEIGH_DELETE:
0a27a2fe 4306 case DPLANE_OP_NEIGH_IP_DELETE:
67e3369e 4307 ret = netlink_neigh_update_ctx(ctx, RTM_DELNEIGH, buf, buflen);
931fa60c 4308 break;
0bbd4ff4 4309 case DPLANE_OP_VTEP_ADD:
67e3369e
JU
4310 ret = netlink_vxlan_flood_update_ctx(ctx, RTM_NEWNEIGH, buf,
4311 buflen);
0bbd4ff4
MS
4312 break;
4313 case DPLANE_OP_VTEP_DELETE:
67e3369e
JU
4314 ret = netlink_vxlan_flood_update_ctx(ctx, RTM_DELNEIGH, buf,
4315 buflen);
0bbd4ff4 4316 break;
e18747a9
PG
4317 case DPLANE_OP_NEIGH_TABLE_UPDATE:
4318 ret = netlink_neigh_table_update_ctx(ctx, buf, buflen);
4319 break;
931fa60c 4320 default:
67e3369e 4321 ret = -1;
931fa60c 4322 }
2232a77c 4323
67e3369e
JU
4324 return ret;
4325}
4326
4327/*
4328 * Update MAC, using dataplane context object.
4329 */
4330
67e3369e
JU
4331enum netlink_msg_status netlink_put_mac_update_msg(struct nl_batch *bth,
4332 struct zebra_dplane_ctx *ctx)
4333{
4334 return netlink_batch_add_msg(bth, ctx, netlink_macfdb_update_ctx,
4335 false);
4336}
4337
67e3369e
JU
4338enum netlink_msg_status
4339netlink_put_neigh_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx)
4340{
4341 return netlink_batch_add_msg(bth, ctx, netlink_neigh_msg_encoder,
4342 false);
6fe2b0e6
CS
4343}
4344
16c628de
MS
4345/*
4346 * MPLS label forwarding table change via netlink interface, using dataplane
4347 * context information.
4348 */
0be6e7d7
JU
4349ssize_t netlink_mpls_multipath_msg_encode(int cmd, struct zebra_dplane_ctx *ctx,
4350 void *buf, size_t buflen)
16c628de
MS
4351{
4352 mpls_lse_t lse;
ee70f629 4353 const struct nhlfe_list_head *head;
f2595bd5 4354 const struct zebra_nhlfe *nhlfe;
16c628de
MS
4355 struct nexthop *nexthop = NULL;
4356 unsigned int nexthop_num;
4357 const char *routedesc;
4358 int route_type;
9a0132a5 4359 struct prefix p = {0};
d4000d7b
DS
4360 struct nlsock *nl =
4361 kernel_netlink_nlsock_lookup(dplane_ctx_get_ns_sock(ctx));
16c628de
MS
4362
4363 struct {
4364 struct nlmsghdr n;
4365 struct rtmsg r;
0be6e7d7
JU
4366 char buf[0];
4367 } *req = buf;
4368
4369 if (buflen < sizeof(*req))
4370 return 0;
16c628de 4371
0be6e7d7 4372 memset(req, 0, sizeof(*req));
16c628de
MS
4373
4374 /*
4375 * Count # nexthops so we can decide whether to use singlepath
4376 * or multipath case.
4377 */
4378 nexthop_num = 0;
ee70f629
MS
4379 head = dplane_ctx_get_nhlfe_list(ctx);
4380 frr_each(nhlfe_list_const, head, nhlfe) {
16c628de
MS
4381 nexthop = nhlfe->nexthop;
4382 if (!nexthop)
4383 continue;
4384 if (cmd == RTM_NEWROUTE) {
4385 /* Count all selected NHLFEs */
4386 if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED)
4387 && CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
4388 nexthop_num++;
4389 } else { /* DEL */
4390 /* Count all installed NHLFEs */
4391 if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)
4392 && CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))
4393 nexthop_num++;
4394 }
4395 }
4396
4397 if ((nexthop_num == 0) ||
4398 (!dplane_ctx_get_best_nhlfe(ctx) && (cmd != RTM_DELROUTE)))
4399 return 0;
4400
0be6e7d7
JU
4401 req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
4402 req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
4403 req->n.nlmsg_type = cmd;
d4000d7b 4404 req->n.nlmsg_pid = nl->snl.nl_pid;
16c628de 4405
0be6e7d7
JU
4406 req->r.rtm_family = AF_MPLS;
4407 req->r.rtm_table = RT_TABLE_MAIN;
4408 req->r.rtm_dst_len = MPLS_LABEL_LEN_BITS;
4409 req->r.rtm_scope = RT_SCOPE_UNIVERSE;
4410 req->r.rtm_type = RTN_UNICAST;
16c628de
MS
4411
4412 if (cmd == RTM_NEWROUTE) {
4413 /* We do a replace to handle update. */
0be6e7d7 4414 req->n.nlmsg_flags |= NLM_F_REPLACE;
16c628de
MS
4415
4416 /* set the protocol value if installing */
4417 route_type = re_type_from_lsp_type(
4418 dplane_ctx_get_best_nhlfe(ctx)->type);
0be6e7d7 4419 req->r.rtm_protocol = zebra2proto(route_type);
16c628de
MS
4420 }
4421
4422 /* Fill destination */
4423 lse = mpls_lse_encode(dplane_ctx_get_in_label(ctx), 0, 0, 1);
0be6e7d7
JU
4424 if (!nl_attr_put(&req->n, buflen, RTA_DST, &lse, sizeof(mpls_lse_t)))
4425 return 0;
16c628de
MS
4426
4427 /* Fill nexthops (paths) based on single-path or multipath. The paths
4428 * chosen depend on the operation.
4429 */
fc608372 4430 if (nexthop_num == 1) {
16c628de
MS
4431 routedesc = "single-path";
4432 _netlink_mpls_debug(cmd, dplane_ctx_get_in_label(ctx),
4433 routedesc);
4434
4435 nexthop_num = 0;
ee70f629 4436 frr_each(nhlfe_list_const, head, nhlfe) {
16c628de
MS
4437 nexthop = nhlfe->nexthop;
4438 if (!nexthop)
4439 continue;
4440
4441 if ((cmd == RTM_NEWROUTE
4442 && (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED)
4443 && CHECK_FLAG(nexthop->flags,
4444 NEXTHOP_FLAG_ACTIVE)))
4445 || (cmd == RTM_DELROUTE
4446 && (CHECK_FLAG(nhlfe->flags,
4447 NHLFE_FLAG_INSTALLED)
4448 && CHECK_FLAG(nexthop->flags,
4449 NEXTHOP_FLAG_FIB)))) {
4450 /* Add the gateway */
0be6e7d7
JU
4451 if (!_netlink_mpls_build_singlepath(
4452 &p, routedesc, nhlfe, &req->n,
4453 &req->r, buflen, cmd))
4454 return false;
16c628de
MS
4455
4456 nexthop_num++;
4457 break;
4458 }
4459 }
4460 } else { /* Multipath case */
312a6bee 4461 struct rtattr *nest;
81793ac1 4462 const union g_addr *src1 = NULL;
16c628de 4463
0be6e7d7
JU
4464 nest = nl_attr_nest(&req->n, buflen, RTA_MULTIPATH);
4465 if (!nest)
4466 return 0;
16c628de
MS
4467
4468 routedesc = "multipath";
4469 _netlink_mpls_debug(cmd, dplane_ctx_get_in_label(ctx),
4470 routedesc);
4471
4472 nexthop_num = 0;
ee70f629 4473 frr_each(nhlfe_list_const, head, nhlfe) {
16c628de
MS
4474 nexthop = nhlfe->nexthop;
4475 if (!nexthop)
4476 continue;
4477
16c628de
MS
4478 if ((cmd == RTM_NEWROUTE
4479 && (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED)
4480 && CHECK_FLAG(nexthop->flags,
4481 NEXTHOP_FLAG_ACTIVE)))
4482 || (cmd == RTM_DELROUTE
4483 && (CHECK_FLAG(nhlfe->flags,
4484 NHLFE_FLAG_INSTALLED)
4485 && CHECK_FLAG(nexthop->flags,
4486 NEXTHOP_FLAG_FIB)))) {
4487 nexthop_num++;
4488
4489 /* Build the multipath */
0be6e7d7
JU
4490 if (!_netlink_mpls_build_multipath(
4491 &p, routedesc, nhlfe, &req->n,
4492 buflen, &req->r, &src1))
4493 return 0;
16c628de
MS
4494 }
4495 }
4496
4497 /* Add the multipath */
0be6e7d7 4498 nl_attr_nest_end(&req->n, nest);
16c628de
MS
4499 }
4500
0be6e7d7 4501 return NLMSG_ALIGN(req->n.nlmsg_len);
16c628de 4502}
506efd37
AK
4503
4504/****************************************************************************
4505* This code was developed in a branch that didn't have dplane APIs for
4506* MAC updates. Hence the use of the legacy style. It will be moved to
4507* the new dplane style pre-merge to master. XXX
4508*/
4509static int netlink_fdb_nh_update(uint32_t nh_id, struct in_addr vtep_ip)
4510{
4511 struct {
4512 struct nlmsghdr n;
4513 struct nhmsg nhm;
4514 char buf[256];
4515 } req;
4516 int cmd = RTM_NEWNEXTHOP;
4517 struct zebra_vrf *zvrf;
4518 struct zebra_ns *zns;
4519
4520 zvrf = zebra_vrf_get_evpn();
506efd37
AK
4521 zns = zvrf->zns;
4522
4523 memset(&req, 0, sizeof(req));
4524
4525 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg));
4526 req.n.nlmsg_flags = NLM_F_REQUEST;
4527 req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE);
4528 req.n.nlmsg_type = cmd;
4529 req.nhm.nh_family = AF_INET;
4530
4531 if (!nl_attr_put32(&req.n, sizeof(req), NHA_ID, nh_id))
4532 return -1;
4533 if (!nl_attr_put(&req.n, sizeof(req), NHA_FDB, NULL, 0))
4534 return -1;
4535 if (!nl_attr_put(&req.n, sizeof(req), NHA_GATEWAY,
4536 &vtep_ip, IPV4_MAX_BYTELEN))
4537 return -1;
4538
4539 if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_EVPN_MH_NH) {
9bcef951
MS
4540 zlog_debug("Tx %s fdb-nh 0x%x %pI4",
4541 nl_msg_type_to_str(cmd), nh_id, &vtep_ip);
506efd37
AK
4542 }
4543
4544 return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
9bfadae8 4545 false);
506efd37
AK
4546}
4547
4548static int netlink_fdb_nh_del(uint32_t nh_id)
4549{
4550 struct {
4551 struct nlmsghdr n;
4552 struct nhmsg nhm;
4553 char buf[256];
4554 } req;
4555 int cmd = RTM_DELNEXTHOP;
4556 struct zebra_vrf *zvrf;
4557 struct zebra_ns *zns;
4558
4559 zvrf = zebra_vrf_get_evpn();
506efd37
AK
4560 zns = zvrf->zns;
4561
4562 memset(&req, 0, sizeof(req));
4563
4564 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg));
4565 req.n.nlmsg_flags = NLM_F_REQUEST;
4566 req.n.nlmsg_type = cmd;
4567 req.nhm.nh_family = AF_UNSPEC;
4568
4569 if (!nl_attr_put32(&req.n, sizeof(req), NHA_ID, nh_id))
4570 return -1;
4571
4572 if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_EVPN_MH_NH) {
4573 zlog_debug("Tx %s fdb-nh 0x%x",
4574 nl_msg_type_to_str(cmd), nh_id);
4575 }
4576
4577 return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
9bfadae8 4578 false);
506efd37
AK
4579}
4580
4581static int netlink_fdb_nhg_update(uint32_t nhg_id, uint32_t nh_cnt,
4582 struct nh_grp *nh_ids)
4583{
4584 struct {
4585 struct nlmsghdr n;
4586 struct nhmsg nhm;
4587 char buf[256];
4588 } req;
4589 int cmd = RTM_NEWNEXTHOP;
4590 struct zebra_vrf *zvrf;
4591 struct zebra_ns *zns;
4592 struct nexthop_grp grp[nh_cnt];
4593 uint32_t i;
4594
4595 zvrf = zebra_vrf_get_evpn();
506efd37
AK
4596 zns = zvrf->zns;
4597
4598 memset(&req, 0, sizeof(req));
4599
4600 req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg));
4601 req.n.nlmsg_flags = NLM_F_REQUEST;
4602 req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE);
4603 req.n.nlmsg_type = cmd;
4604 req.nhm.nh_family = AF_UNSPEC;
4605
4606 if (!nl_attr_put32(&req.n, sizeof(req), NHA_ID, nhg_id))
4607 return -1;
4608 if (!nl_attr_put(&req.n, sizeof(req), NHA_FDB, NULL, 0))
4609 return -1;
4610 memset(&grp, 0, sizeof(grp));
4611 for (i = 0; i < nh_cnt; ++i) {
4612 grp[i].id = nh_ids[i].id;
4613 grp[i].weight = nh_ids[i].weight;
4614 }
4615 if (!nl_attr_put(&req.n, sizeof(req), NHA_GROUP,
4616 grp, nh_cnt * sizeof(struct nexthop_grp)))
4617 return -1;
4618
4619
4620 if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_EVPN_MH_NH) {
4621 char vtep_str[ES_VTEP_LIST_STR_SZ];
9e0c2fd1 4622 char nh_buf[16];
506efd37
AK
4623
4624 vtep_str[0] = '\0';
4625 for (i = 0; i < nh_cnt; ++i) {
9e0c2fd1 4626 snprintf(nh_buf, sizeof(nh_buf), "%u ",
506efd37 4627 grp[i].id);
9e0c2fd1 4628 strlcat(vtep_str, nh_buf, sizeof(vtep_str));
506efd37
AK
4629 }
4630
4631 zlog_debug("Tx %s fdb-nhg 0x%x %s",
4632 nl_msg_type_to_str(cmd), nhg_id, vtep_str);
4633 }
4634
4635 return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
9bfadae8 4636 false);
506efd37
AK
4637}
4638
4639static int netlink_fdb_nhg_del(uint32_t nhg_id)
4640{
4641 return netlink_fdb_nh_del(nhg_id);
4642}
4643
4644int kernel_upd_mac_nh(uint32_t nh_id, struct in_addr vtep_ip)
4645{
4646 return netlink_fdb_nh_update(nh_id, vtep_ip);
4647}
4648
4649int kernel_del_mac_nh(uint32_t nh_id)
4650{
4651 return netlink_fdb_nh_del(nh_id);
4652}
4653
4654int kernel_upd_mac_nhg(uint32_t nhg_id, uint32_t nh_cnt,
4655 struct nh_grp *nh_ids)
4656{
4657 return netlink_fdb_nhg_update(nhg_id, nh_cnt, nh_ids);
4658}
4659
4660int kernel_del_mac_nhg(uint32_t nhg_id)
4661{
4662 return netlink_fdb_nhg_del(nhg_id);
4663}
4664
ddfeb486 4665#endif /* HAVE_NETLINK */