]> git.proxmox.com Git - mirror_frr.git/blame - zebra/zebra_fpm_netlink.c
Merge pull request #6844 from donaldsharp/bestpath_routes
[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 145/*
7cf19069 146 * netlink_nh_info
5adc2528
AS
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 */
7cf19069 152struct netlink_nh_info {
d62a17ae 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;
7cf19069 163};
5adc2528
AS
164
165/*
67ceb408 166 * netlink_route_info
5adc2528
AS
167 *
168 * A structure for holding information for a netlink route message.
169 */
67ceb408 170struct netlink_route_info {
d62a17ae 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 */
7cf19069 183 struct netlink_nh_info nhs[MULTIPATH_NUM];
d62a17ae 184 union g_addr *pref_src;
67ceb408 185};
5adc2528
AS
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 */
67ceb408 195static int netlink_route_info_add_nh(struct netlink_route_info *ri,
9d21b7c6
AD
196 struct nexthop *nexthop,
197 struct route_entry *re)
5adc2528 198{
7cf19069 199 struct netlink_nh_info nhi;
d62a17ae 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 */
67ceb408 278static int netlink_route_info_fill(struct netlink_route_info *ri, int cmd,
d62a17ae 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. */
b0e9567e 342 if (ri->rtm_type != RTN_BLACKHOLE && ri->num_nhs == 0) {
d62a17ae 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 */
67ceb408
DS
356static int netlink_route_info_encode(struct netlink_route_info *ri,
357 char *in_buf, size_t in_buf_len)
5adc2528 358{
d62a17ae 359 size_t bytelen;
360 unsigned int nexthop_num = 0;
361 size_t buf_offset;
7cf19069 362 struct netlink_nh_info *nhi;
9d21b7c6 363 enum fpm_nh_encap_type_t encap;
312a6bee
JU
364 struct rtattr *nest, *inner_nest;
365 struct rtnexthop *rtnh;
9d21b7c6 366 struct vxlan_encap_info_t *vxlan;
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;
312a6bee 403 nl_attr_put32(&req->n, in_buf_len, RTA_TABLE, ri->rtm_table);
6dfcd754
AD
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
312a6bee
JU
410 nl_attr_put(&req->n, in_buf_len, RTA_DST, &ri->prefix->u.prefix,
411 bytelen);
5adc2528 412
d62a17ae 413 req->r.rtm_type = ri->rtm_type;
5adc2528 414
d62a17ae 415 /* Metric. */
416 if (ri->metric)
312a6bee 417 nl_attr_put32(&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) {
312a6bee
JU
426 nl_attr_put(&req->n, in_buf_len, RTA_GATEWAY,
427 nhi->gateway, bytelen);
d62a17ae 428 }
5adc2528 429
d62a17ae 430 if (nhi->if_index) {
312a6bee
JU
431 nl_attr_put32(&req->n, in_buf_len, RTA_OIF,
432 nhi->if_index);
d62a17ae 433 }
5adc2528 434
9d21b7c6 435 encap = nhi->encap_info.encap_type;
58c3cdb9
DS
436 switch (encap) {
437 case FPM_NH_ENCAP_NONE:
438 case FPM_NH_ENCAP_MAX:
439 break;
440 case FPM_NH_ENCAP_VXLAN:
312a6bee
JU
441 nl_attr_put16(&req->n, in_buf_len, RTA_ENCAP_TYPE,
442 encap);
58c3cdb9 443 vxlan = &nhi->encap_info.vxlan_encap;
312a6bee
JU
444 nest = nl_attr_nest(&req->n, in_buf_len, RTA_ENCAP);
445 nl_attr_put32(&req->n, in_buf_len, VXLAN_VNI,
446 vxlan->vni);
447 nl_attr_nest_end(&req->n, nest);
58c3cdb9 448 break;
9d21b7c6
AD
449 }
450
d62a17ae 451 goto done;
5adc2528
AS
452 }
453
d62a17ae 454 /*
455 * Multipath case.
456 */
312a6bee 457 nest = nl_attr_nest(&req->n, in_buf_len, RTA_MULTIPATH);
d62a17ae 458
459 for (nexthop_num = 0; nexthop_num < ri->num_nhs; nexthop_num++) {
312a6bee 460 rtnh = nl_attr_rtnh(&req->n, in_buf_len);
d62a17ae 461 nhi = &ri->nhs[nexthop_num];
462
312a6bee
JU
463 if (nhi->gateway)
464 nl_attr_put(&req->n, in_buf_len, RTA_GATEWAY,
465 nhi->gateway, bytelen);
d62a17ae 466
467 if (nhi->if_index) {
468 rtnh->rtnh_ifindex = nhi->if_index;
469 }
470
9d21b7c6 471 encap = nhi->encap_info.encap_type;
58c3cdb9
DS
472 switch (encap) {
473 case FPM_NH_ENCAP_NONE:
474 case FPM_NH_ENCAP_MAX:
475 break;
476 case FPM_NH_ENCAP_VXLAN:
312a6bee
JU
477 nl_attr_put16(&req->n, in_buf_len, RTA_ENCAP_TYPE,
478 encap);
58c3cdb9 479 vxlan = &nhi->encap_info.vxlan_encap;
312a6bee
JU
480 inner_nest =
481 nl_attr_nest(&req->n, in_buf_len, RTA_ENCAP);
482 nl_attr_put32(&req->n, in_buf_len, VXLAN_VNI,
483 vxlan->vni);
484 nl_attr_nest_end(&req->n, inner_nest);
58c3cdb9 485 break;
9d21b7c6
AD
486 }
487
312a6bee 488 nl_attr_rtnh_end(&req->n, rtnh);
5adc2528
AS
489 }
490
312a6bee
JU
491 nl_attr_nest_end(&req->n, nest);
492 assert(nest->rta_len > RTA_LENGTH(0));
5adc2528
AS
493
494done:
495
d62a17ae 496 if (ri->pref_src) {
312a6bee
JU
497 nl_attr_put(&req->n, in_buf_len, RTA_PREFSRC, &ri->pref_src,
498 bytelen);
d62a17ae 499 }
5adc2528 500
d62a17ae 501 assert(req->n.nlmsg_len < in_buf_len);
502 return req->n.nlmsg_len;
5adc2528
AS
503}
504
505/*
506 * zfpm_log_route_info
507 *
508 * Helper function to log the information in a route_info structure.
509 */
67ceb408
DS
510static void zfpm_log_route_info(struct netlink_route_info *ri,
511 const char *label)
5adc2528 512{
7cf19069 513 struct netlink_nh_info *nhi;
d62a17ae 514 unsigned int i;
515
516 zfpm_debug("%s : %s %s/%d, Proto: %s, Metric: %u", label,
517 nl_msg_type_to_str(ri->nlmsg_type),
518 prefix_addr_to_a(ri->prefix), ri->prefix->prefixlen,
519 nl_rtproto_to_str(ri->rtm_protocol),
520 ri->metric ? *ri->metric : 0);
521
522 for (i = 0; i < ri->num_nhs; i++) {
523 nhi = &ri->nhs[i];
9d21b7c6 524 zfpm_debug(" Intf: %u, Gateway: %s, Recursive: %s, Type: %s, Encap type: %s",
d62a17ae 525 nhi->if_index, addr_to_a(ri->af, nhi->gateway),
526 nhi->recursive ? "yes" : "no",
9d21b7c6
AD
527 nexthop_type_to_str(nhi->type),
528 fpm_nh_encap_type_to_str(nhi->encap_info.encap_type)
529 );
d62a17ae 530 }
5adc2528
AS
531}
532
533/*
534 * zfpm_netlink_encode_route
535 *
536 * Create a netlink message corresponding to the given route in the
537 * given buffer space.
538 *
539 * Returns the number of bytes written to the buffer. 0 or a negative
540 * value indicates an error.
541 */
d62a17ae 542int zfpm_netlink_encode_route(int cmd, rib_dest_t *dest, struct route_entry *re,
543 char *in_buf, size_t in_buf_len)
5adc2528 544{
67ceb408 545 struct netlink_route_info ri_space, *ri;
5adc2528 546
d62a17ae 547 ri = &ri_space;
5adc2528 548
d62a17ae 549 if (!netlink_route_info_fill(ri, cmd, dest, re))
550 return 0;
5adc2528 551
15569c58 552 zfpm_log_route_info(ri, __func__);
5adc2528 553
d62a17ae 554 return netlink_route_info_encode(ri, in_buf, in_buf_len);
5adc2528 555}
ddfeb486 556
9da60d0a
AD
557/*
558 * zfpm_netlink_encode_mac
559 *
560 * Create a netlink message corresponding to the given MAC.
561 *
562 * Returns the number of bytes written to the buffer. 0 or a negative
563 * value indicates an error.
564 */
565int zfpm_netlink_encode_mac(struct fpm_mac_info_t *mac, char *in_buf,
566 size_t in_buf_len)
567{
568 char buf1[ETHER_ADDR_STRLEN];
569 size_t buf_offset;
570
c5431822 571 struct macmsg {
9da60d0a
AD
572 struct nlmsghdr hdr;
573 struct ndmsg ndm;
574 char buf[0];
575 } *req;
576 req = (void *)in_buf;
577
c5431822 578 buf_offset = offsetof(struct macmsg, buf);
9da60d0a
AD
579 if (in_buf_len < buf_offset)
580 return 0;
581 memset(req, 0, buf_offset);
582
583 /* Construct nlmsg header */
584 req->hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
585 req->hdr.nlmsg_type = CHECK_FLAG(mac->fpm_flags, ZEBRA_MAC_DELETE_FPM) ?
586 RTM_DELNEIGH : RTM_NEWNEIGH;
587 req->hdr.nlmsg_flags = NLM_F_REQUEST;
588 if (req->hdr.nlmsg_type == RTM_NEWNEIGH)
589 req->hdr.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE);
590
591 /* Construct ndmsg */
592 req->ndm.ndm_family = AF_BRIDGE;
593 req->ndm.ndm_ifindex = mac->vxlan_if;
594
595 req->ndm.ndm_state = NUD_REACHABLE;
596 req->ndm.ndm_flags |= NTF_SELF | NTF_MASTER;
597 if (CHECK_FLAG(mac->zebra_flags,
598 (ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW)))
599 req->ndm.ndm_state |= NUD_NOARP;
600 else
601 req->ndm.ndm_flags |= NTF_EXT_LEARNED;
602
603 /* Add attributes */
312a6bee
JU
604 nl_attr_put(&req->hdr, in_buf_len, NDA_LLADDR, &mac->macaddr, 6);
605 nl_attr_put(&req->hdr, in_buf_len, NDA_DST, &mac->r_vtep_ip, 4);
606 nl_attr_put32(&req->hdr, in_buf_len, NDA_MASTER, mac->svi_if);
607 nl_attr_put32(&req->hdr, in_buf_len, NDA_VNI, mac->vni);
9da60d0a
AD
608
609 assert(req->hdr.nlmsg_len < in_buf_len);
610
611 zfpm_debug("Tx %s family %s ifindex %u MAC %s DEST %s",
612 nl_msg_type_to_str(req->hdr.nlmsg_type),
613 nl_family_to_str(req->ndm.ndm_family), req->ndm.ndm_ifindex,
614 prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)),
615 inet_ntoa(mac->r_vtep_ip));
616
617 return req->hdr.nlmsg_len;
618}
619
ddfeb486 620#endif /* HAVE_NETLINK */