]> git.proxmox.com Git - mirror_frr.git/blame - zebra/zebra_fpm_netlink.c
Merge pull request #5451 from opensourcerouting/rcu-log
[mirror_frr.git] / zebra / zebra_fpm_netlink.c
CommitLineData
5adc2528
AS
1/*
2 * Code for encoding/decoding FPM messages that are in netlink format.
3 *
4 * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
5 * Copyright (C) 2012 by Open Source Routing.
6 * Copyright (C) 2012 by Internet Systems Consortium, Inc. ("ISC")
7 *
8 * This file is part of GNU Zebra.
9 *
10 * GNU Zebra is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2, or (at your option) any
13 * later version.
14 *
15 * GNU Zebra is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
19 *
896014f4
DL
20 * You should have received a copy of the GNU General Public License along
21 * with this program; see the file COPYING; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
5adc2528
AS
23 */
24
25#include <zebra.h>
26
ddfeb486
DL
27#ifdef HAVE_NETLINK
28
5adc2528
AS
29#include "log.h"
30#include "rib.h"
82f97584 31#include "vty.h"
e3be0432 32#include "prefix.h"
5adc2528 33
1fdc9eae 34#include "zebra/zserv.h"
b3f2b590 35#include "zebra/zebra_router.h"
85a75f1e 36#include "zebra/zebra_dplane.h"
1fdc9eae 37#include "zebra/zebra_ns.h"
38#include "zebra/zebra_vrf.h"
39#include "zebra/kernel_netlink.h"
40#include "zebra/rt_netlink.h"
fb018d25 41#include "nexthop.h"
5adc2528 42
1fdc9eae 43#include "zebra/zebra_fpm_private.h"
9d21b7c6 44#include "zebra/zebra_vxlan_private.h"
5adc2528
AS
45
46/*
47 * addr_to_a
48 *
49 * Returns string representation of an address of the given AF.
50 */
d7c0a89a 51static inline const char *addr_to_a(uint8_t af, void *addr)
5adc2528 52{
d62a17ae 53 if (!addr)
54 return "<No address>";
55
56 switch (af) {
57
58 case AF_INET:
59 return inet_ntoa(*((struct in_addr *)addr));
d62a17ae 60 case AF_INET6:
61 return inet6_ntoa(*((struct in6_addr *)addr));
d62a17ae 62 default:
63 return "<Addr in unknown AF>";
d62a17ae 64 }
5adc2528
AS
65}
66
67/*
68 * prefix_addr_to_a
69 *
70 * Convience wrapper that returns a human-readable string for the
71 * address in a prefix.
72 */
d62a17ae 73static const char *prefix_addr_to_a(struct prefix *prefix)
5adc2528 74{
d62a17ae 75 if (!prefix)
76 return "<No address>";
5adc2528 77
d62a17ae 78 return addr_to_a(prefix->family, &prefix->u.prefix);
5adc2528
AS
79}
80
81/*
82 * af_addr_size
83 *
84 * The size of an address in a given address family.
85 */
d7c0a89a 86static size_t af_addr_size(uint8_t af)
5adc2528 87{
d62a17ae 88 switch (af) {
89
90 case AF_INET:
91 return 4;
d62a17ae 92 case AF_INET6:
93 return 16;
d62a17ae 94 default:
95 assert(0);
96 return 16;
97 }
5adc2528
AS
98}
99
9d21b7c6
AD
100/*
101 * We plan to use RTA_ENCAP_TYPE attribute for VxLAN encap as well.
102 * Currently, values 0 to 8 for this attribute are used by lwtunnel_encap_types
103 * So, we cannot use these values for VxLAN encap.
104 */
105enum fpm_nh_encap_type_t {
106 FPM_NH_ENCAP_NONE = 0,
107 FPM_NH_ENCAP_VXLAN = 100,
108 FPM_NH_ENCAP_MAX,
109};
110
111/*
112 * fpm_nh_encap_type_to_str
113 */
114static const char *fpm_nh_encap_type_to_str(enum fpm_nh_encap_type_t encap_type)
115{
116 switch (encap_type) {
117 case FPM_NH_ENCAP_NONE:
118 return "none";
119
120 case FPM_NH_ENCAP_VXLAN:
121 return "VxLAN";
122
123 case FPM_NH_ENCAP_MAX:
124 return "invalid";
125 }
126
127 return "invalid";
128}
129
130struct vxlan_encap_info_t {
131 vni_t vni;
132};
133
134enum vxlan_encap_info_type_t {
135 VXLAN_VNI = 0,
136};
137
138struct fpm_nh_encap_info_t {
139 enum fpm_nh_encap_type_t encap_type;
140 union {
141 struct vxlan_encap_info_t vxlan_encap;
142 };
143};
144
5adc2528
AS
145/*
146 * netlink_nh_info_t
147 *
148 * Holds information about a single nexthop for netlink. These info
149 * structures are transient and may contain pointers into rib
150 * data structures for convenience.
151 */
d62a17ae 152typedef struct netlink_nh_info_t_ {
153 uint32_t if_index;
154 union g_addr *gateway;
155
156 /*
157 * Information from the struct nexthop from which this nh was
158 * derived. For debug purposes only.
159 */
160 int recursive;
161 enum nexthop_types_t type;
9d21b7c6 162 struct fpm_nh_encap_info_t encap_info;
5adc2528
AS
163} netlink_nh_info_t;
164
165/*
166 * netlink_route_info_t
167 *
168 * A structure for holding information for a netlink route message.
169 */
d62a17ae 170typedef struct netlink_route_info_t_ {
171 uint16_t nlmsg_type;
d7c0a89a 172 uint8_t rtm_type;
d62a17ae 173 uint32_t rtm_table;
d7c0a89a
QY
174 uint8_t rtm_protocol;
175 uint8_t af;
d62a17ae 176 struct prefix *prefix;
177 uint32_t *metric;
178 unsigned int num_nhs;
179
180 /*
181 * Nexthop structures
182 */
183 netlink_nh_info_t nhs[MULTIPATH_NUM];
184 union g_addr *pref_src;
5adc2528
AS
185} netlink_route_info_t;
186
187/*
188 * netlink_route_info_add_nh
189 *
190 * Add information about the given nexthop to the given route info
191 * structure.
192 *
2951a7a4 193 * Returns true if a nexthop was added, false otherwise.
5adc2528 194 */
d62a17ae 195static int netlink_route_info_add_nh(netlink_route_info_t *ri,
9d21b7c6
AD
196 struct nexthop *nexthop,
197 struct route_entry *re)
5adc2528 198{
d62a17ae 199 netlink_nh_info_t nhi;
200 union g_addr *src;
9d21b7c6 201 zebra_l3vni_t *zl3vni = NULL;
d62a17ae 202
203 memset(&nhi, 0, sizeof(nhi));
204 src = NULL;
205
7e3a1ec7 206 if (ri->num_nhs >= (int)array_size(ri->nhs))
d62a17ae 207 return 0;
208
209 nhi.recursive = nexthop->rparent ? 1 : 0;
210 nhi.type = nexthop->type;
211 nhi.if_index = nexthop->ifindex;
212
213 if (nexthop->type == NEXTHOP_TYPE_IPV4
214 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) {
215 nhi.gateway = &nexthop->gate;
975a328e 216 if (nexthop->src.ipv4.s_addr != INADDR_ANY)
d62a17ae 217 src = &nexthop->src;
218 }
219
220 if (nexthop->type == NEXTHOP_TYPE_IPV6
221 || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) {
222 nhi.gateway = &nexthop->gate;
223 }
224
225 if (nexthop->type == NEXTHOP_TYPE_IFINDEX) {
975a328e 226 if (nexthop->src.ipv4.s_addr != INADDR_ANY)
d62a17ae 227 src = &nexthop->src;
228 }
229
230 if (!nhi.gateway && nhi.if_index == 0)
231 return 0;
232
9d21b7c6
AD
233 if (re && CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) {
234 nhi.encap_info.encap_type = FPM_NH_ENCAP_VXLAN;
235
cc839074 236 zl3vni = zl3vni_from_vrf(nexthop->vrf_id);
9d21b7c6
AD
237 if (zl3vni && is_l3vni_oper_up(zl3vni)) {
238
239 /* Add VNI to VxLAN encap info */
240 nhi.encap_info.vxlan_encap.vni = zl3vni->vni;
241 }
242 }
243
d62a17ae 244 /*
245 * We have a valid nhi. Copy the structure over to the route_info.
246 */
247 ri->nhs[ri->num_nhs] = nhi;
248 ri->num_nhs++;
249
250 if (src && !ri->pref_src)
251 ri->pref_src = src;
252
253 return 1;
5adc2528
AS
254}
255
256/*
257 * netlink_proto_from_route_type
258 */
d7c0a89a 259static uint8_t netlink_proto_from_route_type(int type)
5adc2528 260{
d62a17ae 261 switch (type) {
262 case ZEBRA_ROUTE_KERNEL:
263 case ZEBRA_ROUTE_CONNECT:
264 return RTPROT_KERNEL;
265
266 default:
267 return RTPROT_ZEBRA;
268 }
5adc2528
AS
269}
270
271/*
272 * netlink_route_info_fill
273 *
274 * Fill out the route information object from the given route.
275 *
2951a7a4 276 * Returns true on success and false on failure.
5adc2528 277 */
d62a17ae 278static int netlink_route_info_fill(netlink_route_info_t *ri, int cmd,
279 rib_dest_t *dest, struct route_entry *re)
5adc2528 280{
d62a17ae 281 struct nexthop *nexthop;
6dfcd754 282 struct zebra_vrf *zvrf;
d62a17ae 283
284 memset(ri, 0, sizeof(*ri));
285
286 ri->prefix = rib_dest_prefix(dest);
287 ri->af = rib_dest_af(dest);
288
289 ri->nlmsg_type = cmd;
6dfcd754
AD
290 zvrf = rib_dest_vrf(dest);
291 if (zvrf)
292 ri->rtm_table = zvrf->table_id;
d62a17ae 293 ri->rtm_protocol = RTPROT_UNSPEC;
294
295 /*
296 * An RTM_DELROUTE need not be accompanied by any nexthops,
297 * particularly in our communication with the FPM.
298 */
299 if (cmd == RTM_DELROUTE && !re)
300 return 1;
301
302 if (!re) {
5e81f5dd 303 zfpm_debug("%s: Expected non-NULL re pointer", __func__);
d62a17ae 304 return 0;
305 }
306
307 ri->rtm_protocol = netlink_proto_from_route_type(re->type);
a8309422 308 ri->rtm_type = RTN_UNICAST;
d62a17ae 309 ri->metric = &re->metric;
310
c415d895 311 for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) {
b3f2b590 312 if (ri->num_nhs >= zrouter.multipath_num)
d62a17ae 313 break;
314
315 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
316 continue;
317
a8309422
DL
318 if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) {
319 switch (nexthop->bh_type) {
320 case BLACKHOLE_ADMINPROHIB:
321 ri->rtm_type = RTN_PROHIBIT;
322 break;
323 case BLACKHOLE_REJECT:
324 ri->rtm_type = RTN_UNREACHABLE;
325 break;
326 case BLACKHOLE_NULL:
327 default:
328 ri->rtm_type = RTN_BLACKHOLE;
329 break;
330 }
a8309422
DL
331 }
332
d62a17ae 333 if ((cmd == RTM_NEWROUTE
334 && CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
335 || (cmd == RTM_DELROUTE
677c1dd5 336 && CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED))) {
9d21b7c6 337 netlink_route_info_add_nh(ri, nexthop, re);
d62a17ae 338 }
339 }
340
341 /* If there is no useful nexthop then return. */
342 if (ri->num_nhs == 0) {
343 zfpm_debug("netlink_encode_route(): No useful nexthop.");
344 return 0;
345 }
346
347 return 1;
5adc2528
AS
348}
349
350/*
351 * netlink_route_info_encode
352 *
353 * Returns the number of bytes written to the buffer. 0 or a negative
354 * value indicates an error.
355 */
d62a17ae 356static int netlink_route_info_encode(netlink_route_info_t *ri, char *in_buf,
357 size_t in_buf_len)
5adc2528 358{
d62a17ae 359 size_t bytelen;
360 unsigned int nexthop_num = 0;
361 size_t buf_offset;
362 netlink_nh_info_t *nhi;
9d21b7c6
AD
363 enum fpm_nh_encap_type_t encap;
364 struct rtattr *nest;
365 struct vxlan_encap_info_t *vxlan;
366 int nest_len;
5adc2528 367
d62a17ae 368 struct {
369 struct nlmsghdr n;
370 struct rtmsg r;
371 char buf[1];
372 } * req;
5adc2528 373
d62a17ae 374 req = (void *)in_buf;
5adc2528 375
d62a17ae 376 buf_offset = ((char *)req->buf) - ((char *)req);
5adc2528 377
d62a17ae 378 if (in_buf_len < buf_offset) {
379 assert(0);
380 return 0;
381 }
5adc2528 382
d62a17ae 383 memset(req, 0, buf_offset);
5adc2528 384
d62a17ae 385 bytelen = af_addr_size(ri->af);
5adc2528 386
d62a17ae 387 req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
388 req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
389 req->n.nlmsg_type = ri->nlmsg_type;
390 req->r.rtm_family = ri->af;
6dfcd754
AD
391
392 /*
393 * rtm_table field is a uchar field which can accomodate table_id less
394 * than 256.
395 * To support table id greater than 255, if the table_id is greater than
396 * 255, set rtm_table to RT_TABLE_UNSPEC and add RTA_TABLE attribute
397 * with 32 bit value as the table_id.
398 */
399 if (ri->rtm_table < 256)
400 req->r.rtm_table = ri->rtm_table;
401 else {
402 req->r.rtm_table = RT_TABLE_UNSPEC;
403 addattr32(&req->n, in_buf_len, RTA_TABLE, ri->rtm_table);
404 }
405
d62a17ae 406 req->r.rtm_dst_len = ri->prefix->prefixlen;
407 req->r.rtm_protocol = ri->rtm_protocol;
408 req->r.rtm_scope = RT_SCOPE_UNIVERSE;
5adc2528 409
d62a17ae 410 addattr_l(&req->n, in_buf_len, RTA_DST, &ri->prefix->u.prefix, bytelen);
5adc2528 411
d62a17ae 412 req->r.rtm_type = ri->rtm_type;
5adc2528 413
d62a17ae 414 /* Metric. */
415 if (ri->metric)
416 addattr32(&req->n, in_buf_len, RTA_PRIORITY, *ri->metric);
5adc2528 417
d62a17ae 418 if (ri->num_nhs == 0)
419 goto done;
5adc2528 420
d62a17ae 421 if (ri->num_nhs == 1) {
422 nhi = &ri->nhs[0];
5adc2528 423
d62a17ae 424 if (nhi->gateway) {
425 addattr_l(&req->n, in_buf_len, RTA_GATEWAY,
426 nhi->gateway, bytelen);
427 }
5adc2528 428
d62a17ae 429 if (nhi->if_index) {
430 addattr32(&req->n, in_buf_len, RTA_OIF, nhi->if_index);
431 }
5adc2528 432
9d21b7c6
AD
433 encap = nhi->encap_info.encap_type;
434 if (encap > FPM_NH_ENCAP_NONE) {
435 addattr_l(&req->n, in_buf_len, RTA_ENCAP_TYPE, &encap,
436 sizeof(uint16_t));
437 switch (encap) {
438 case FPM_NH_ENCAP_NONE:
439 break;
440 case FPM_NH_ENCAP_VXLAN:
441 vxlan = &nhi->encap_info.vxlan_encap;
442 nest = addattr_nest(&req->n, in_buf_len,
443 RTA_ENCAP);
444 addattr32(&req->n, in_buf_len, VXLAN_VNI,
445 vxlan->vni);
446 addattr_nest_end(&req->n, nest);
447 break;
448 case FPM_NH_ENCAP_MAX:
449 break;
450 }
451 }
452
d62a17ae 453 goto done;
5adc2528
AS
454 }
455
d62a17ae 456 /*
457 * Multipath case.
458 */
459 char buf[NL_PKT_BUF_SIZE];
460 struct rtattr *rta = (void *)buf;
461 struct rtnexthop *rtnh;
462
463 rta->rta_type = RTA_MULTIPATH;
464 rta->rta_len = RTA_LENGTH(0);
465 rtnh = RTA_DATA(rta);
466
467 for (nexthop_num = 0; nexthop_num < ri->num_nhs; nexthop_num++) {
468 nhi = &ri->nhs[nexthop_num];
469
470 rtnh->rtnh_len = sizeof(*rtnh);
471 rtnh->rtnh_flags = 0;
472 rtnh->rtnh_hops = 0;
473 rtnh->rtnh_ifindex = 0;
474 rta->rta_len += rtnh->rtnh_len;
475
476 if (nhi->gateway) {
477 rta_addattr_l(rta, sizeof(buf), RTA_GATEWAY,
478 nhi->gateway, bytelen);
479 rtnh->rtnh_len += sizeof(struct rtattr) + bytelen;
480 }
481
482 if (nhi->if_index) {
483 rtnh->rtnh_ifindex = nhi->if_index;
484 }
485
9d21b7c6
AD
486 encap = nhi->encap_info.encap_type;
487 if (encap > FPM_NH_ENCAP_NONE) {
488 rta_addattr_l(rta, sizeof(buf), RTA_ENCAP_TYPE,
489 &encap, sizeof(uint16_t));
490 rtnh->rtnh_len += sizeof(struct rtattr) +
491 sizeof(uint16_t);
492 switch (encap) {
493 case FPM_NH_ENCAP_NONE:
494 break;
495 case FPM_NH_ENCAP_VXLAN:
496 vxlan = &nhi->encap_info.vxlan_encap;
497 nest = rta_nest(rta, sizeof(buf), RTA_ENCAP);
498 rta_addattr_l(rta, sizeof(buf), VXLAN_VNI,
499 &vxlan->vni, sizeof(uint32_t));
500 nest_len = rta_nest_end(rta, nest);
501 rtnh->rtnh_len += nest_len;
502 break;
503 case FPM_NH_ENCAP_MAX:
504 break;
505 }
506 }
507
d62a17ae 508 rtnh = RTNH_NEXT(rtnh);
5adc2528
AS
509 }
510
d62a17ae 511 assert(rta->rta_len > RTA_LENGTH(0));
512 addattr_l(&req->n, in_buf_len, RTA_MULTIPATH, RTA_DATA(rta),
513 RTA_PAYLOAD(rta));
5adc2528
AS
514
515done:
516
d62a17ae 517 if (ri->pref_src) {
518 addattr_l(&req->n, in_buf_len, RTA_PREFSRC, &ri->pref_src,
519 bytelen);
520 }
5adc2528 521
d62a17ae 522 assert(req->n.nlmsg_len < in_buf_len);
523 return req->n.nlmsg_len;
5adc2528
AS
524}
525
526/*
527 * zfpm_log_route_info
528 *
529 * Helper function to log the information in a route_info structure.
530 */
d62a17ae 531static void zfpm_log_route_info(netlink_route_info_t *ri, const char *label)
5adc2528 532{
d62a17ae 533 netlink_nh_info_t *nhi;
534 unsigned int i;
535
536 zfpm_debug("%s : %s %s/%d, Proto: %s, Metric: %u", label,
537 nl_msg_type_to_str(ri->nlmsg_type),
538 prefix_addr_to_a(ri->prefix), ri->prefix->prefixlen,
539 nl_rtproto_to_str(ri->rtm_protocol),
540 ri->metric ? *ri->metric : 0);
541
542 for (i = 0; i < ri->num_nhs; i++) {
543 nhi = &ri->nhs[i];
9d21b7c6 544 zfpm_debug(" Intf: %u, Gateway: %s, Recursive: %s, Type: %s, Encap type: %s",
d62a17ae 545 nhi->if_index, addr_to_a(ri->af, nhi->gateway),
546 nhi->recursive ? "yes" : "no",
9d21b7c6
AD
547 nexthop_type_to_str(nhi->type),
548 fpm_nh_encap_type_to_str(nhi->encap_info.encap_type)
549 );
d62a17ae 550 }
5adc2528
AS
551}
552
553/*
554 * zfpm_netlink_encode_route
555 *
556 * Create a netlink message corresponding to the given route in the
557 * given buffer space.
558 *
559 * Returns the number of bytes written to the buffer. 0 or a negative
560 * value indicates an error.
561 */
d62a17ae 562int zfpm_netlink_encode_route(int cmd, rib_dest_t *dest, struct route_entry *re,
563 char *in_buf, size_t in_buf_len)
5adc2528 564{
d62a17ae 565 netlink_route_info_t ri_space, *ri;
5adc2528 566
d62a17ae 567 ri = &ri_space;
5adc2528 568
d62a17ae 569 if (!netlink_route_info_fill(ri, cmd, dest, re))
570 return 0;
5adc2528 571
15569c58 572 zfpm_log_route_info(ri, __func__);
5adc2528 573
d62a17ae 574 return netlink_route_info_encode(ri, in_buf, in_buf_len);
5adc2528 575}
ddfeb486 576
9da60d0a
AD
577/*
578 * zfpm_netlink_encode_mac
579 *
580 * Create a netlink message corresponding to the given MAC.
581 *
582 * Returns the number of bytes written to the buffer. 0 or a negative
583 * value indicates an error.
584 */
585int zfpm_netlink_encode_mac(struct fpm_mac_info_t *mac, char *in_buf,
586 size_t in_buf_len)
587{
588 char buf1[ETHER_ADDR_STRLEN];
589 size_t buf_offset;
590
c5431822 591 struct macmsg {
9da60d0a
AD
592 struct nlmsghdr hdr;
593 struct ndmsg ndm;
594 char buf[0];
595 } *req;
596 req = (void *)in_buf;
597
c5431822 598 buf_offset = offsetof(struct macmsg, buf);
9da60d0a
AD
599 if (in_buf_len < buf_offset)
600 return 0;
601 memset(req, 0, buf_offset);
602
603 /* Construct nlmsg header */
604 req->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
605 req->hdr.nlmsg_type = CHECK_FLAG(mac->fpm_flags, ZEBRA_MAC_DELETE_FPM) ?
606 RTM_DELNEIGH : RTM_NEWNEIGH;
607 req->hdr.nlmsg_flags = NLM_F_REQUEST;
608 if (req->hdr.nlmsg_type == RTM_NEWNEIGH)
609 req->hdr.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE);
610
611 /* Construct ndmsg */
612 req->ndm.ndm_family = AF_BRIDGE;
613 req->ndm.ndm_ifindex = mac->vxlan_if;
614
615 req->ndm.ndm_state = NUD_REACHABLE;
616 req->ndm.ndm_flags |= NTF_SELF | NTF_MASTER;
617 if (CHECK_FLAG(mac->zebra_flags,
618 (ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW)))
619 req->ndm.ndm_state |= NUD_NOARP;
620 else
621 req->ndm.ndm_flags |= NTF_EXT_LEARNED;
622
623 /* Add attributes */
624 addattr_l(&req->hdr, in_buf_len, NDA_LLADDR, &mac->macaddr, 6);
625 addattr_l(&req->hdr, in_buf_len, NDA_DST, &mac->r_vtep_ip, 4);
626 addattr32(&req->hdr, in_buf_len, NDA_MASTER, mac->svi_if);
627 addattr32(&req->hdr, in_buf_len, NDA_VNI, mac->vni);
628
629 assert(req->hdr.nlmsg_len < in_buf_len);
630
631 zfpm_debug("Tx %s family %s ifindex %u MAC %s DEST %s",
632 nl_msg_type_to_str(req->hdr.nlmsg_type),
633 nl_family_to_str(req->ndm.ndm_family), req->ndm.ndm_ifindex,
634 prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)),
635 inet_ntoa(mac->r_vtep_ip));
636
637 return req->hdr.nlmsg_len;
638}
639
ddfeb486 640#endif /* HAVE_NETLINK */