]> git.proxmox.com Git - mirror_frr.git/blame - zebra/zebra_fpm_netlink.c
*: Replace __PRETTY_FUNCTION__/__FUNCTION__ to __func__
[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) {
303 zfpm_debug("%s: Expected non-NULL re pointer",
304 __PRETTY_FUNCTION__);
305 return 0;
306 }
307
308 ri->rtm_protocol = netlink_proto_from_route_type(re->type);
a8309422 309 ri->rtm_type = RTN_UNICAST;
d62a17ae 310 ri->metric = &re->metric;
311
c415d895 312 for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) {
b3f2b590 313 if (ri->num_nhs >= zrouter.multipath_num)
d62a17ae 314 break;
315
316 if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
317 continue;
318
a8309422
DL
319 if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE) {
320 switch (nexthop->bh_type) {
321 case BLACKHOLE_ADMINPROHIB:
322 ri->rtm_type = RTN_PROHIBIT;
323 break;
324 case BLACKHOLE_REJECT:
325 ri->rtm_type = RTN_UNREACHABLE;
326 break;
327 case BLACKHOLE_NULL:
328 default:
329 ri->rtm_type = RTN_BLACKHOLE;
330 break;
331 }
a8309422
DL
332 }
333
d62a17ae 334 if ((cmd == RTM_NEWROUTE
335 && CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE))
336 || (cmd == RTM_DELROUTE
677c1dd5 337 && CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED))) {
9d21b7c6 338 netlink_route_info_add_nh(ri, nexthop, re);
d62a17ae 339 }
340 }
341
342 /* If there is no useful nexthop then return. */
343 if (ri->num_nhs == 0) {
344 zfpm_debug("netlink_encode_route(): No useful nexthop.");
345 return 0;
346 }
347
348 return 1;
5adc2528
AS
349}
350
351/*
352 * netlink_route_info_encode
353 *
354 * Returns the number of bytes written to the buffer. 0 or a negative
355 * value indicates an error.
356 */
d62a17ae 357static int netlink_route_info_encode(netlink_route_info_t *ri, char *in_buf,
358 size_t in_buf_len)
5adc2528 359{
d62a17ae 360 size_t bytelen;
361 unsigned int nexthop_num = 0;
362 size_t buf_offset;
363 netlink_nh_info_t *nhi;
9d21b7c6
AD
364 enum fpm_nh_encap_type_t encap;
365 struct rtattr *nest;
366 struct vxlan_encap_info_t *vxlan;
367 int nest_len;
5adc2528 368
d62a17ae 369 struct {
370 struct nlmsghdr n;
371 struct rtmsg r;
372 char buf[1];
373 } * req;
5adc2528 374
d62a17ae 375 req = (void *)in_buf;
5adc2528 376
d62a17ae 377 buf_offset = ((char *)req->buf) - ((char *)req);
5adc2528 378
d62a17ae 379 if (in_buf_len < buf_offset) {
380 assert(0);
381 return 0;
382 }
5adc2528 383
d62a17ae 384 memset(req, 0, buf_offset);
5adc2528 385
d62a17ae 386 bytelen = af_addr_size(ri->af);
5adc2528 387
d62a17ae 388 req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
389 req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
390 req->n.nlmsg_type = ri->nlmsg_type;
391 req->r.rtm_family = ri->af;
6dfcd754
AD
392
393 /*
394 * rtm_table field is a uchar field which can accomodate table_id less
395 * than 256.
396 * To support table id greater than 255, if the table_id is greater than
397 * 255, set rtm_table to RT_TABLE_UNSPEC and add RTA_TABLE attribute
398 * with 32 bit value as the table_id.
399 */
400 if (ri->rtm_table < 256)
401 req->r.rtm_table = ri->rtm_table;
402 else {
403 req->r.rtm_table = RT_TABLE_UNSPEC;
404 addattr32(&req->n, in_buf_len, RTA_TABLE, ri->rtm_table);
405 }
406
d62a17ae 407 req->r.rtm_dst_len = ri->prefix->prefixlen;
408 req->r.rtm_protocol = ri->rtm_protocol;
409 req->r.rtm_scope = RT_SCOPE_UNIVERSE;
5adc2528 410
d62a17ae 411 addattr_l(&req->n, in_buf_len, RTA_DST, &ri->prefix->u.prefix, bytelen);
5adc2528 412
d62a17ae 413 req->r.rtm_type = ri->rtm_type;
5adc2528 414
d62a17ae 415 /* Metric. */
416 if (ri->metric)
417 addattr32(&req->n, in_buf_len, RTA_PRIORITY, *ri->metric);
5adc2528 418
d62a17ae 419 if (ri->num_nhs == 0)
420 goto done;
5adc2528 421
d62a17ae 422 if (ri->num_nhs == 1) {
423 nhi = &ri->nhs[0];
5adc2528 424
d62a17ae 425 if (nhi->gateway) {
426 addattr_l(&req->n, in_buf_len, RTA_GATEWAY,
427 nhi->gateway, bytelen);
428 }
5adc2528 429
d62a17ae 430 if (nhi->if_index) {
431 addattr32(&req->n, in_buf_len, RTA_OIF, nhi->if_index);
432 }
5adc2528 433
9d21b7c6
AD
434 encap = nhi->encap_info.encap_type;
435 if (encap > FPM_NH_ENCAP_NONE) {
436 addattr_l(&req->n, in_buf_len, RTA_ENCAP_TYPE, &encap,
437 sizeof(uint16_t));
438 switch (encap) {
439 case FPM_NH_ENCAP_NONE:
440 break;
441 case FPM_NH_ENCAP_VXLAN:
442 vxlan = &nhi->encap_info.vxlan_encap;
443 nest = addattr_nest(&req->n, in_buf_len,
444 RTA_ENCAP);
445 addattr32(&req->n, in_buf_len, VXLAN_VNI,
446 vxlan->vni);
447 addattr_nest_end(&req->n, nest);
448 break;
449 case FPM_NH_ENCAP_MAX:
450 break;
451 }
452 }
453
d62a17ae 454 goto done;
5adc2528
AS
455 }
456
d62a17ae 457 /*
458 * Multipath case.
459 */
460 char buf[NL_PKT_BUF_SIZE];
461 struct rtattr *rta = (void *)buf;
462 struct rtnexthop *rtnh;
463
464 rta->rta_type = RTA_MULTIPATH;
465 rta->rta_len = RTA_LENGTH(0);
466 rtnh = RTA_DATA(rta);
467
468 for (nexthop_num = 0; nexthop_num < ri->num_nhs; nexthop_num++) {
469 nhi = &ri->nhs[nexthop_num];
470
471 rtnh->rtnh_len = sizeof(*rtnh);
472 rtnh->rtnh_flags = 0;
473 rtnh->rtnh_hops = 0;
474 rtnh->rtnh_ifindex = 0;
475 rta->rta_len += rtnh->rtnh_len;
476
477 if (nhi->gateway) {
478 rta_addattr_l(rta, sizeof(buf), RTA_GATEWAY,
479 nhi->gateway, bytelen);
480 rtnh->rtnh_len += sizeof(struct rtattr) + bytelen;
481 }
482
483 if (nhi->if_index) {
484 rtnh->rtnh_ifindex = nhi->if_index;
485 }
486
9d21b7c6
AD
487 encap = nhi->encap_info.encap_type;
488 if (encap > FPM_NH_ENCAP_NONE) {
489 rta_addattr_l(rta, sizeof(buf), RTA_ENCAP_TYPE,
490 &encap, sizeof(uint16_t));
491 rtnh->rtnh_len += sizeof(struct rtattr) +
492 sizeof(uint16_t);
493 switch (encap) {
494 case FPM_NH_ENCAP_NONE:
495 break;
496 case FPM_NH_ENCAP_VXLAN:
497 vxlan = &nhi->encap_info.vxlan_encap;
498 nest = rta_nest(rta, sizeof(buf), RTA_ENCAP);
499 rta_addattr_l(rta, sizeof(buf), VXLAN_VNI,
500 &vxlan->vni, sizeof(uint32_t));
501 nest_len = rta_nest_end(rta, nest);
502 rtnh->rtnh_len += nest_len;
503 break;
504 case FPM_NH_ENCAP_MAX:
505 break;
506 }
507 }
508
d62a17ae 509 rtnh = RTNH_NEXT(rtnh);
5adc2528
AS
510 }
511
d62a17ae 512 assert(rta->rta_len > RTA_LENGTH(0));
513 addattr_l(&req->n, in_buf_len, RTA_MULTIPATH, RTA_DATA(rta),
514 RTA_PAYLOAD(rta));
5adc2528
AS
515
516done:
517
d62a17ae 518 if (ri->pref_src) {
519 addattr_l(&req->n, in_buf_len, RTA_PREFSRC, &ri->pref_src,
520 bytelen);
521 }
5adc2528 522
d62a17ae 523 assert(req->n.nlmsg_len < in_buf_len);
524 return req->n.nlmsg_len;
5adc2528
AS
525}
526
527/*
528 * zfpm_log_route_info
529 *
530 * Helper function to log the information in a route_info structure.
531 */
d62a17ae 532static void zfpm_log_route_info(netlink_route_info_t *ri, const char *label)
5adc2528 533{
d62a17ae 534 netlink_nh_info_t *nhi;
535 unsigned int i;
536
537 zfpm_debug("%s : %s %s/%d, Proto: %s, Metric: %u", label,
538 nl_msg_type_to_str(ri->nlmsg_type),
539 prefix_addr_to_a(ri->prefix), ri->prefix->prefixlen,
540 nl_rtproto_to_str(ri->rtm_protocol),
541 ri->metric ? *ri->metric : 0);
542
543 for (i = 0; i < ri->num_nhs; i++) {
544 nhi = &ri->nhs[i];
9d21b7c6 545 zfpm_debug(" Intf: %u, Gateway: %s, Recursive: %s, Type: %s, Encap type: %s",
d62a17ae 546 nhi->if_index, addr_to_a(ri->af, nhi->gateway),
547 nhi->recursive ? "yes" : "no",
9d21b7c6
AD
548 nexthop_type_to_str(nhi->type),
549 fpm_nh_encap_type_to_str(nhi->encap_info.encap_type)
550 );
d62a17ae 551 }
5adc2528
AS
552}
553
554/*
555 * zfpm_netlink_encode_route
556 *
557 * Create a netlink message corresponding to the given route in the
558 * given buffer space.
559 *
560 * Returns the number of bytes written to the buffer. 0 or a negative
561 * value indicates an error.
562 */
d62a17ae 563int zfpm_netlink_encode_route(int cmd, rib_dest_t *dest, struct route_entry *re,
564 char *in_buf, size_t in_buf_len)
5adc2528 565{
d62a17ae 566 netlink_route_info_t ri_space, *ri;
5adc2528 567
d62a17ae 568 ri = &ri_space;
5adc2528 569
d62a17ae 570 if (!netlink_route_info_fill(ri, cmd, dest, re))
571 return 0;
5adc2528 572
15569c58 573 zfpm_log_route_info(ri, __func__);
5adc2528 574
d62a17ae 575 return netlink_route_info_encode(ri, in_buf, in_buf_len);
5adc2528 576}
ddfeb486 577
9da60d0a
AD
578/*
579 * zfpm_netlink_encode_mac
580 *
581 * Create a netlink message corresponding to the given MAC.
582 *
583 * Returns the number of bytes written to the buffer. 0 or a negative
584 * value indicates an error.
585 */
586int zfpm_netlink_encode_mac(struct fpm_mac_info_t *mac, char *in_buf,
587 size_t in_buf_len)
588{
589 char buf1[ETHER_ADDR_STRLEN];
590 size_t buf_offset;
591
c5431822 592 struct macmsg {
9da60d0a
AD
593 struct nlmsghdr hdr;
594 struct ndmsg ndm;
595 char buf[0];
596 } *req;
597 req = (void *)in_buf;
598
c5431822 599 buf_offset = offsetof(struct macmsg, buf);
9da60d0a
AD
600 if (in_buf_len < buf_offset)
601 return 0;
602 memset(req, 0, buf_offset);
603
604 /* Construct nlmsg header */
605 req->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
606 req->hdr.nlmsg_type = CHECK_FLAG(mac->fpm_flags, ZEBRA_MAC_DELETE_FPM) ?
607 RTM_DELNEIGH : RTM_NEWNEIGH;
608 req->hdr.nlmsg_flags = NLM_F_REQUEST;
609 if (req->hdr.nlmsg_type == RTM_NEWNEIGH)
610 req->hdr.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE);
611
612 /* Construct ndmsg */
613 req->ndm.ndm_family = AF_BRIDGE;
614 req->ndm.ndm_ifindex = mac->vxlan_if;
615
616 req->ndm.ndm_state = NUD_REACHABLE;
617 req->ndm.ndm_flags |= NTF_SELF | NTF_MASTER;
618 if (CHECK_FLAG(mac->zebra_flags,
619 (ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW)))
620 req->ndm.ndm_state |= NUD_NOARP;
621 else
622 req->ndm.ndm_flags |= NTF_EXT_LEARNED;
623
624 /* Add attributes */
625 addattr_l(&req->hdr, in_buf_len, NDA_LLADDR, &mac->macaddr, 6);
626 addattr_l(&req->hdr, in_buf_len, NDA_DST, &mac->r_vtep_ip, 4);
627 addattr32(&req->hdr, in_buf_len, NDA_MASTER, mac->svi_if);
628 addattr32(&req->hdr, in_buf_len, NDA_VNI, mac->vni);
629
630 assert(req->hdr.nlmsg_len < in_buf_len);
631
632 zfpm_debug("Tx %s family %s ifindex %u MAC %s DEST %s",
633 nl_msg_type_to_str(req->hdr.nlmsg_type),
634 nl_family_to_str(req->ndm.ndm_family), req->ndm.ndm_ifindex,
635 prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)),
636 inet_ntoa(mac->r_vtep_ip));
637
638 return req->hdr.nlmsg_len;
639}
640
ddfeb486 641#endif /* HAVE_NETLINK */