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: *
20: * You should have received a copy of the GNU General Public License
21: * along with GNU Zebra; see the file COPYING. If not, write to the Free
22: * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
23: * 02111-1307, USA.
24: */
25:
26: #include <zebra.h>
27:
28: #include "log.h"
29: #include "rib.h"
30:
31: #include "rt_netlink.h"
32:
33: #include "zebra_fpm_private.h"
34:
35: /*
36: * addr_to_a
37: *
38: * Returns string representation of an address of the given AF.
39: */
40: static inline const char *
41: addr_to_a (u_char af, void *addr)
42: {
43: if (!addr)
44: return "<No address>";
45:
46: switch (af)
47: {
48:
49: case AF_INET:
50: return inet_ntoa (*((struct in_addr *) addr));
51:
52: #ifdef HAVE_IPV6
53: case AF_INET6:
54: return inet6_ntoa (*((struct in6_addr *) addr));
55: #endif
56:
57: default:
58: return "<Addr in unknown AF>";
59: }
60: }
61:
62: /*
63: * prefix_addr_to_a
64: *
65: * Convience wrapper that returns a human-readable string for the
66: * address in a prefix.
67: */
68: static const char *
69: prefix_addr_to_a (struct prefix *prefix)
70: {
71: if (!prefix)
72: return "<No address>";
73:
74: return addr_to_a (prefix->family, &prefix->u.prefix);
75: }
76:
77: /*
78: * af_addr_size
79: *
80: * The size of an address in a given address family.
81: */
82: static size_t
83: af_addr_size (u_char af)
84: {
85: switch (af)
86: {
87:
88: case AF_INET:
89: return 4;
90:
91: #ifdef HAVE_IPV6
92: case AF_INET6:
93: return 16;
94: #endif
95:
96: default:
97: assert(0);
98: return 16;
99: }
100: }
101:
102: /*
103: * netlink_nh_info_t
104: *
105: * Holds information about a single nexthop for netlink. These info
106: * structures are transient and may contain pointers into rib
107: * data structures for convenience.
108: */
109: typedef struct netlink_nh_info_t_
110: {
111: uint32_t if_index;
112: union g_addr *gateway;
113:
114: /*
115: * Information from the struct nexthop from which this nh was
116: * derived. For debug purposes only.
117: */
118: int recursive;
119: enum nexthop_types_t type;
120: } netlink_nh_info_t;
121:
122: /*
123: * netlink_route_info_t
124: *
125: * A structure for holding information for a netlink route message.
126: */
127: typedef struct netlink_route_info_t_
128: {
129: uint16_t nlmsg_type;
130: u_char rtm_type;
131: uint32_t rtm_table;
132: u_char rtm_protocol;
133: u_char af;
134: struct prefix *prefix;
135: uint32_t *metric;
136: int num_nhs;
137:
138: /*
139: * Nexthop structures. We keep things simple for now by enforcing a
140: * maximum of 64 in case MULTIPATH_NUM is 0;
141: */
142: netlink_nh_info_t nhs[MAX (MULTIPATH_NUM, 64)];
143: union g_addr *pref_src;
144: } netlink_route_info_t;
145:
146: /*
147: * netlink_route_info_add_nh
148: *
149: * Add information about the given nexthop to the given route info
150: * structure.
151: *
152: * Returns TRUE if a nexthop was added, FALSE otherwise.
153: */
154: static int
155: netlink_route_info_add_nh (netlink_route_info_t *ri, struct nexthop *nexthop)
156: {
157: netlink_nh_info_t nhi;
158: union g_addr *src;
159:
160: memset (&nhi, 0, sizeof (nhi));
161: src = NULL;
162:
163: if (ri->num_nhs >= (int) ZEBRA_NUM_OF (ri->nhs))
164: return 0;
165:
166: if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE))
167: {
168: nhi.recursive = 1;
169: nhi.type = nexthop->rtype;
170: nhi.if_index = nexthop->rifindex;
171:
172: if (nexthop->rtype == NEXTHOP_TYPE_IPV4
173: || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX)
174: {
175: nhi.gateway = &nexthop->rgate;
176: if (nexthop->src.ipv4.s_addr)
177: src = &nexthop->src;
178: }
179:
180: #ifdef HAVE_IPV6
181: if (nexthop->rtype == NEXTHOP_TYPE_IPV6
182: || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX
183: || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME)
184: {
185: nhi.gateway = &nexthop->rgate;
186: }
187: #endif /* HAVE_IPV6 */
188:
189: if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX
190: || nexthop->rtype == NEXTHOP_TYPE_IFNAME)
191: {
192: if (nexthop->src.ipv4.s_addr)
193: src = &nexthop->src;
194: }
195:
196: goto done;
197: }
198:
199: nhi.recursive = 0;
200: nhi.type = nexthop->type;
201: nhi.if_index = nexthop->ifindex;
202:
203: if (nexthop->type == NEXTHOP_TYPE_IPV4
204: || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
205: {
206: nhi.gateway = &nexthop->gate;
207: if (nexthop->src.ipv4.s_addr)
208: src = &nexthop->src;
209: }
210:
211: #ifdef HAVE_IPV6
212: if (nexthop->type == NEXTHOP_TYPE_IPV6
213: || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
214: || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
215: {
216: nhi.gateway = &nexthop->gate;
217: }
218: #endif /* HAVE_IPV6 */
219:
220: if (nexthop->type == NEXTHOP_TYPE_IFINDEX
221: || nexthop->type == NEXTHOP_TYPE_IFNAME)
222: {
223: if (nexthop->src.ipv4.s_addr)
224: src = &nexthop->src;
225: }
226:
227: /*
228: * Fall through...
229: */
230:
231: done:
232: if (!nhi.gateway && nhi.if_index == 0)
233: return 0;
234:
235: /*
236: * We have a valid nhi. Copy the structure over to the route_info.
237: */
238: ri->nhs[ri->num_nhs] = nhi;
239: ri->num_nhs++;
240:
241: if (src && !ri->pref_src)
242: ri->pref_src = src;
243:
244: return 1;
245: }
246:
247: /*
248: * netlink_proto_from_route_type
249: */
250: static u_char
251: netlink_proto_from_route_type (int type)
252: {
253: switch (type)
254: {
255: case ZEBRA_ROUTE_KERNEL:
256: case ZEBRA_ROUTE_CONNECT:
257: return RTPROT_KERNEL;
258:
259: default:
260: return RTPROT_ZEBRA;
261: }
262: }
263:
264: /*
265: * netlink_route_info_fill
266: *
267: * Fill out the route information object from the given route.
268: *
269: * Returns TRUE on success and FALSE on failure.
270: */
271: static int
272: netlink_route_info_fill (netlink_route_info_t *ri, int cmd,
273: rib_dest_t *dest, struct rib *rib)
274: {
275: struct nexthop *nexthop = NULL;
276: int discard;
277:
278: memset (ri, 0, sizeof (*ri));
279:
280: ri->prefix = rib_dest_prefix (dest);
281: ri->af = rib_dest_af (dest);
282:
283: ri->nlmsg_type = cmd;
284: ri->rtm_table = rib_dest_vrf (dest)->id;
285: ri->rtm_protocol = RTPROT_UNSPEC;
286:
287: /*
288: * An RTM_DELROUTE need not be accompanied by any nexthops,
289: * particularly in our communication with the FPM.
290: */
291: if (cmd == RTM_DELROUTE && !rib)
292: goto skip;
293:
294: if (rib)
295: ri->rtm_protocol = netlink_proto_from_route_type (rib->type);
296:
297: if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) || (rib->flags & ZEBRA_FLAG_REJECT))
298: discard = 1;
299: else
300: discard = 0;
301:
302: if (cmd == RTM_NEWROUTE)
303: {
304: if (discard)
305: {
306: if (rib->flags & ZEBRA_FLAG_BLACKHOLE)
307: ri->rtm_type = RTN_BLACKHOLE;
308: else if (rib->flags & ZEBRA_FLAG_REJECT)
309: ri->rtm_type = RTN_UNREACHABLE;
310: else
311: assert (0);
312: }
313: else
314: ri->rtm_type = RTN_UNICAST;
315: }
316:
317: ri->metric = &rib->metric;
318:
319: if (discard)
320: {
321: goto skip;
322: }
323:
324: /* Multipath case. */
325: if (rib->nexthop_active_num == 1 || MULTIPATH_NUM == 1)
326: {
327: for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next)
328: {
329:
330: if ((cmd == RTM_NEWROUTE
331: && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
332: || (cmd == RTM_DELROUTE
333: && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
334: {
335: netlink_route_info_add_nh (ri, nexthop);
336: break;
337: }
338: }
339: }
340: else
341: {
342: for (nexthop = rib->nexthop;
343: nexthop && (MULTIPATH_NUM == 0 || ri->num_nhs < MULTIPATH_NUM);
344: nexthop = nexthop->next)
345: {
346: if ((cmd == RTM_NEWROUTE
347: && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))
348: || (cmd == RTM_DELROUTE
349: && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))
350: {
351: netlink_route_info_add_nh (ri, nexthop);
352: }
353: }
354: }
355:
356: /* If there is no useful nexthop then return. */
357: if (ri->num_nhs == 0)
358: {
359: zfpm_debug ("netlink_encode_route(): No useful nexthop.");
360: return 0;
361: }
362:
363: skip:
364: return 1;
365: }
366:
367: /*
368: * netlink_route_info_encode
369: *
370: * Returns the number of bytes written to the buffer. 0 or a negative
371: * value indicates an error.
372: */
373: static int
374: netlink_route_info_encode (netlink_route_info_t *ri, char *in_buf,
375: size_t in_buf_len)
376: {
377: int bytelen;
378: int nexthop_num = 0;
379: size_t buf_offset;
380: netlink_nh_info_t *nhi;
381:
382: struct
383: {
384: struct nlmsghdr n;
385: struct rtmsg r;
386: char buf[1];
387: } *req;
388:
389: req = (void *) in_buf;
390:
391: buf_offset = ((char *) req->buf) - ((char *) req);
392:
393: if (in_buf_len < buf_offset) {
394: assert(0);
395: return 0;
396: }
397:
398: memset (req, 0, buf_offset);
399:
400: bytelen = af_addr_size (ri->af);
401:
402: req->n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg));
403: req->n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST;
404: req->n.nlmsg_type = ri->nlmsg_type;
405: req->r.rtm_family = ri->af;
406: req->r.rtm_table = ri->rtm_table;
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;
410:
411: addattr_l (&req->n, in_buf_len, RTA_DST, &ri->prefix->u.prefix, bytelen);
412:
413: req->r.rtm_type = ri->rtm_type;
414:
415: /* Metric. */
416: if (ri->metric)
417: addattr32 (&req->n, in_buf_len, RTA_PRIORITY, *ri->metric);
418:
419: if (ri->num_nhs == 0)
420: goto done;
421:
422: if (ri->num_nhs == 1)
423: {
424: nhi = &ri->nhs[0];
425:
426: if (nhi->gateway)
427: {
428: addattr_l (&req->n, in_buf_len, RTA_GATEWAY, nhi->gateway,
429: bytelen);
430: }
431:
432: if (nhi->if_index)
433: {
434: addattr32 (&req->n, in_buf_len, RTA_OIF, nhi->if_index);
435: }
436:
437: goto done;
438:
439: }
440:
441: /*
442: * Multipath case.
443: */
444: char buf[NL_PKT_BUF_SIZE];
445: struct rtattr *rta = (void *) buf;
446: struct rtnexthop *rtnh;
447:
448: rta->rta_type = RTA_MULTIPATH;
449: rta->rta_len = RTA_LENGTH (0);
450: rtnh = RTA_DATA (rta);
451:
452: for (nexthop_num = 0; nexthop_num < ri->num_nhs; nexthop_num++)
453: {
454: nhi = &ri->nhs[nexthop_num];
455:
456: rtnh->rtnh_len = sizeof (*rtnh);
457: rtnh->rtnh_flags = 0;
458: rtnh->rtnh_hops = 0;
459: rtnh->rtnh_ifindex = 0;
460: rta->rta_len += rtnh->rtnh_len;
461:
462: if (nhi->gateway)
463: {
464: rta_addattr_l (rta, sizeof (buf), RTA_GATEWAY, nhi->gateway, bytelen);
465: rtnh->rtnh_len += sizeof (struct rtattr) + bytelen;
466: }
467:
468: if (nhi->if_index)
469: {
470: rtnh->rtnh_ifindex = nhi->if_index;
471: }
472:
473: rtnh = RTNH_NEXT (rtnh);
474: }
475:
476: assert (rta->rta_len > RTA_LENGTH (0));
477: addattr_l (&req->n, in_buf_len, RTA_MULTIPATH, RTA_DATA (rta),
478: RTA_PAYLOAD (rta));
479:
480: done:
481:
482: if (ri->pref_src)
483: {
484: addattr_l (&req->n, in_buf_len, RTA_PREFSRC, &ri->pref_src, bytelen);
485: }
486:
487: assert (req->n.nlmsg_len < in_buf_len);
488: return req->n.nlmsg_len;
489: }
490:
491: /*
492: * zfpm_log_route_info
493: *
494: * Helper function to log the information in a route_info structure.
495: */
496: static void
497: zfpm_log_route_info (netlink_route_info_t *ri, const char *label)
498: {
499: netlink_nh_info_t *nhi;
500: int i;
501:
502: zfpm_debug ("%s : %s %s/%d, Proto: %s, Metric: %u", label,
503: nl_msg_type_to_str (ri->nlmsg_type),
504: prefix_addr_to_a (ri->prefix), ri->prefix->prefixlen,
505: nl_rtproto_to_str (ri->rtm_protocol),
506: ri->metric ? *ri->metric : 0);
507:
508: for (i = 0; i < ri->num_nhs; i++)
509: {
510: nhi = &ri->nhs[i];
511: zfpm_debug(" Intf: %u, Gateway: %s, Recursive: %s, Type: %s",
512: nhi->if_index, addr_to_a (ri->af, nhi->gateway),
513: nhi->recursive ? "yes" : "no",
514: nexthop_type_to_str (nhi->type));
515: }
516: }
517:
518: /*
519: * zfpm_netlink_encode_route
520: *
521: * Create a netlink message corresponding to the given route in the
522: * given buffer space.
523: *
524: * Returns the number of bytes written to the buffer. 0 or a negative
525: * value indicates an error.
526: */
527: int
528: zfpm_netlink_encode_route (int cmd, rib_dest_t *dest, struct rib *rib,
529: char *in_buf, size_t in_buf_len)
530: {
531: netlink_route_info_t ri_space, *ri;
532:
533: ri = &ri_space;
534:
535: if (!netlink_route_info_fill (ri, cmd, dest, rib))
536: return 0;
537:
538: zfpm_log_route_info (ri, __FUNCTION__);
539:
540: return netlink_route_info_encode (ri, in_buf, in_buf_len);
541: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>