rt.c 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123
  1. /*-
  2. * SPDX-License-Identifier: BSD-2-Clause
  3. *
  4. * Copyright (c) 2021 Ng Peng Nam Sean
  5. * Copyright (c) 2022 Alexander V. Chernikov <melifaro@FreeBSD.org>
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * 2. Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  17. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  19. * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  20. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  22. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  23. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  24. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  25. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  26. * SUCH DAMAGE.
  27. */
  28. #include <sys/cdefs.h>
  29. #include "opt_inet.h"
  30. #include "opt_inet6.h"
  31. #include "opt_route.h"
  32. #include <sys/types.h>
  33. #include <sys/malloc.h>
  34. #include <sys/rmlock.h>
  35. #include <sys/socket.h>
  36. #include <net/if.h>
  37. #include <net/route.h>
  38. #include <net/route/nhop.h>
  39. #include <net/route/route_ctl.h>
  40. #include <net/route/route_var.h>
  41. #include <netinet6/scope6_var.h>
  42. #include <netlink/netlink.h>
  43. #include <netlink/netlink_ctl.h>
  44. #include <netlink/netlink_route.h>
  45. #include <netlink/route/route_var.h>
  46. #define DEBUG_MOD_NAME nl_route
  47. #define DEBUG_MAX_LEVEL LOG_DEBUG3
  48. #include <netlink/netlink_debug.h>
  49. _DECLARE_DEBUG(LOG_INFO);
  50. static unsigned char
  51. get_rtm_type(const struct nhop_object *nh)
  52. {
  53. int nh_flags = nh->nh_flags;
  54. /* Use the fact that nhg runtime flags are only NHF_MULTIPATH */
  55. if (nh_flags & NHF_BLACKHOLE)
  56. return (RTN_BLACKHOLE);
  57. else if (nh_flags & NHF_REJECT)
  58. return (RTN_PROHIBIT);
  59. return (RTN_UNICAST);
  60. }
  61. static uint8_t
  62. nl_get_rtm_protocol(const struct nhop_object *nh)
  63. {
  64. #ifdef ROUTE_MPATH
  65. if (NH_IS_NHGRP(nh)) {
  66. const struct nhgrp_object *nhg = (const struct nhgrp_object *)nh;
  67. uint8_t origin = nhgrp_get_origin(nhg);
  68. if (origin != RTPROT_UNSPEC)
  69. return (origin);
  70. nh = nhg->nhops[0];
  71. }
  72. #endif
  73. uint8_t origin = nhop_get_origin(nh);
  74. if (origin != RTPROT_UNSPEC)
  75. return (origin);
  76. /* TODO: remove guesswork once all kernel users fill in origin */
  77. int rt_flags = nhop_get_rtflags(nh);
  78. if (rt_flags & RTF_PROTO1)
  79. return (RTPROT_ZEBRA);
  80. if (rt_flags & RTF_STATIC)
  81. return (RTPROT_STATIC);
  82. return (RTPROT_KERNEL);
  83. }
  84. static int
  85. get_rtmsg_type_from_rtsock(int cmd)
  86. {
  87. switch (cmd) {
  88. case RTM_ADD:
  89. case RTM_CHANGE:
  90. case RTM_GET:
  91. return NL_RTM_NEWROUTE;
  92. case RTM_DELETE:
  93. return NL_RTM_DELROUTE;
  94. }
  95. return (0);
  96. }
  97. /*
  98. * fibnum heuristics
  99. *
  100. * if (dump && rtm_table == 0 && !rta_table) RT_ALL_FIBS
  101. * msg rtm_table RTA_TABLE result
  102. * RTM_GETROUTE/dump 0 - RT_ALL_FIBS
  103. * RTM_GETROUTE/dump 1 - 1
  104. * RTM_GETROUTE/get 0 - 0
  105. *
  106. */
  107. static struct nhop_object *
  108. rc_get_nhop(const struct rib_cmd_info *rc)
  109. {
  110. return ((rc->rc_cmd == RTM_DELETE) ? rc->rc_nh_old : rc->rc_nh_new);
  111. }
  112. static void
  113. dump_rc_nhop_gw(struct nl_writer *nw, const struct nhop_object *nh)
  114. {
  115. #ifdef INET6
  116. int upper_family;
  117. #endif
  118. switch (nhop_get_neigh_family(nh)) {
  119. case AF_LINK:
  120. /* onlink prefix, skip */
  121. break;
  122. case AF_INET:
  123. nlattr_add(nw, NL_RTA_GATEWAY, 4, &nh->gw4_sa.sin_addr);
  124. break;
  125. #ifdef INET6
  126. case AF_INET6:
  127. upper_family = nhop_get_upper_family(nh);
  128. if (upper_family == AF_INET6) {
  129. struct in6_addr gw6 = nh->gw6_sa.sin6_addr;
  130. in6_clearscope(&gw6);
  131. nlattr_add(nw, NL_RTA_GATEWAY, 16, &gw6);
  132. } else if (upper_family == AF_INET) {
  133. /* IPv4 over IPv6 */
  134. struct in6_addr gw6 = nh->gw6_sa.sin6_addr;
  135. in6_clearscope(&gw6);
  136. char buf[20];
  137. struct rtvia *via = (struct rtvia *)&buf[0];
  138. via->rtvia_family = AF_INET6;
  139. memcpy(via->rtvia_addr, &gw6, 16);
  140. nlattr_add(nw, NL_RTA_VIA, 17, via);
  141. }
  142. break;
  143. #endif
  144. }
  145. }
  146. static void
  147. dump_rc_nhop_mtu(struct nl_writer *nw, const struct nhop_object *nh)
  148. {
  149. int nla_len = sizeof(struct nlattr) * 2 + sizeof(uint32_t);
  150. struct nlattr *nla = nlmsg_reserve_data(nw, nla_len, struct nlattr);
  151. if (nla == NULL)
  152. return;
  153. nla->nla_type = NL_RTA_METRICS;
  154. nla->nla_len = nla_len;
  155. nla++;
  156. nla->nla_type = NL_RTAX_MTU;
  157. nla->nla_len = sizeof(struct nlattr) + sizeof(uint32_t);
  158. *((uint32_t *)(nla + 1)) = nh->nh_mtu;
  159. }
  160. #ifdef ROUTE_MPATH
  161. static void
  162. dump_rc_nhg(struct nl_writer *nw, const struct nhgrp_object *nhg, struct rtmsg *rtm)
  163. {
  164. uint32_t uidx = nhgrp_get_uidx(nhg);
  165. uint32_t num_nhops;
  166. const struct weightened_nhop *wn = nhgrp_get_nhops(nhg, &num_nhops);
  167. uint32_t base_rtflags = nhop_get_rtflags(wn[0].nh);
  168. if (uidx != 0)
  169. nlattr_add_u32(nw, NL_RTA_NH_ID, uidx);
  170. nlattr_add_u32(nw, NL_RTA_KNH_ID, nhgrp_get_idx(nhg));
  171. nlattr_add_u32(nw, NL_RTA_RTFLAGS, base_rtflags);
  172. int off = nlattr_add_nested(nw, NL_RTA_MULTIPATH);
  173. if (off == 0)
  174. return;
  175. for (int i = 0; i < num_nhops; i++) {
  176. int nh_off = nlattr_save_offset(nw);
  177. struct rtnexthop *rtnh = nlmsg_reserve_object(nw, struct rtnexthop);
  178. if (rtnh == NULL)
  179. return;
  180. rtnh->rtnh_flags = 0;
  181. rtnh->rtnh_ifindex = if_getindex(wn[i].nh->nh_ifp);
  182. rtnh->rtnh_hops = wn[i].weight;
  183. dump_rc_nhop_gw(nw, wn[i].nh);
  184. uint32_t rtflags = nhop_get_rtflags(wn[i].nh);
  185. if (rtflags != base_rtflags)
  186. nlattr_add_u32(nw, NL_RTA_RTFLAGS, rtflags);
  187. if (rtflags & RTF_FIXEDMTU)
  188. dump_rc_nhop_mtu(nw, wn[i].nh);
  189. rtnh = nlattr_restore_offset(nw, nh_off, struct rtnexthop);
  190. /*
  191. * nlattr_add() allocates 4-byte aligned storage, no need to aligh
  192. * length here
  193. * */
  194. rtnh->rtnh_len = nlattr_save_offset(nw) - nh_off;
  195. }
  196. nlattr_set_len(nw, off);
  197. }
  198. #endif
  199. static void
  200. dump_rc_nhop(struct nl_writer *nw, const struct route_nhop_data *rnd, struct rtmsg *rtm)
  201. {
  202. #ifdef ROUTE_MPATH
  203. if (NH_IS_NHGRP(rnd->rnd_nhop)) {
  204. dump_rc_nhg(nw, rnd->rnd_nhgrp, rtm);
  205. return;
  206. }
  207. #endif
  208. const struct nhop_object *nh = rnd->rnd_nhop;
  209. uint32_t rtflags = nhop_get_rtflags(nh);
  210. /*
  211. * IPv4 over IPv6
  212. * ('RTA_VIA', {'family': 10, 'addr': 'fe80::20c:29ff:fe67:2dd'}), ('RTA_OIF', 2),
  213. * IPv4 w/ gw
  214. * ('RTA_GATEWAY', '172.16.107.131'), ('RTA_OIF', 2)],
  215. * Direct route:
  216. * ('RTA_OIF', 2)
  217. */
  218. if (nh->nh_flags & NHF_GATEWAY)
  219. dump_rc_nhop_gw(nw, nh);
  220. uint32_t uidx = nhop_get_uidx(nh);
  221. if (uidx != 0)
  222. nlattr_add_u32(nw, NL_RTA_NH_ID, uidx);
  223. nlattr_add_u32(nw, NL_RTA_KNH_ID, nhop_get_idx(nh));
  224. nlattr_add_u32(nw, NL_RTA_RTFLAGS, rtflags);
  225. if (rtflags & RTF_FIXEDMTU)
  226. dump_rc_nhop_mtu(nw, nh);
  227. uint32_t nh_expire = nhop_get_expire(nh);
  228. if (nh_expire > 0)
  229. nlattr_add_u32(nw, NL_RTA_EXPIRES, nh_expire - time_uptime);
  230. /* In any case, fill outgoing interface */
  231. nlattr_add_u32(nw, NL_RTA_OIF, if_getindex(nh->nh_ifp));
  232. if (rnd->rnd_weight != RT_DEFAULT_WEIGHT)
  233. nlattr_add_u32(nw, NL_RTA_WEIGHT, rnd->rnd_weight);
  234. }
  235. /*
  236. * Dumps output from a rib command into an rtmsg
  237. */
  238. static int
  239. dump_px(uint32_t fibnum, const struct nlmsghdr *hdr,
  240. const struct rtentry *rt, struct route_nhop_data *rnd,
  241. struct nl_writer *nw)
  242. {
  243. struct rtmsg *rtm;
  244. int error = 0;
  245. NET_EPOCH_ASSERT();
  246. if (!nlmsg_reply(nw, hdr, sizeof(struct rtmsg)))
  247. goto enomem;
  248. int family = rt_get_family(rt);
  249. int rtm_off = nlattr_save_offset(nw);
  250. rtm = nlmsg_reserve_object(nw, struct rtmsg);
  251. rtm->rtm_family = family;
  252. rtm->rtm_dst_len = 0;
  253. rtm->rtm_src_len = 0;
  254. rtm->rtm_tos = 0;
  255. if (fibnum < 255)
  256. rtm->rtm_table = (unsigned char)fibnum;
  257. rtm->rtm_scope = RT_SCOPE_UNIVERSE;
  258. rtm->rtm_protocol = nl_get_rtm_protocol(rnd->rnd_nhop);
  259. rtm->rtm_type = get_rtm_type(rnd->rnd_nhop);
  260. nlattr_add_u32(nw, NL_RTA_TABLE, fibnum);
  261. int plen = 0;
  262. #if defined(INET) || defined(INET6)
  263. uint32_t scopeid;
  264. #endif
  265. switch (family) {
  266. #ifdef INET
  267. case AF_INET:
  268. {
  269. struct in_addr addr;
  270. rt_get_inet_prefix_plen(rt, &addr, &plen, &scopeid);
  271. nlattr_add(nw, NL_RTA_DST, 4, &addr);
  272. break;
  273. }
  274. #endif
  275. #ifdef INET6
  276. case AF_INET6:
  277. {
  278. struct in6_addr addr;
  279. rt_get_inet6_prefix_plen(rt, &addr, &plen, &scopeid);
  280. nlattr_add(nw, NL_RTA_DST, 16, &addr);
  281. break;
  282. }
  283. #endif
  284. default:
  285. FIB_LOG(LOG_NOTICE, fibnum, family, "unsupported rt family: %d", family);
  286. error = EAFNOSUPPORT;
  287. goto flush;
  288. }
  289. rtm = nlattr_restore_offset(nw, rtm_off, struct rtmsg);
  290. if (plen > 0)
  291. rtm->rtm_dst_len = plen;
  292. dump_rc_nhop(nw, rnd, rtm);
  293. if (nlmsg_end(nw))
  294. return (0);
  295. enomem:
  296. error = ENOMEM;
  297. flush:
  298. nlmsg_abort(nw);
  299. return (error);
  300. }
  301. static int
  302. family_to_group(int family)
  303. {
  304. switch (family) {
  305. case AF_INET:
  306. return (RTNLGRP_IPV4_ROUTE);
  307. case AF_INET6:
  308. return (RTNLGRP_IPV6_ROUTE);
  309. }
  310. return (0);
  311. }
  312. static void
  313. report_operation(uint32_t fibnum, struct rib_cmd_info *rc,
  314. struct nlpcb *nlp, struct nlmsghdr *hdr)
  315. {
  316. struct nl_writer nw = {};
  317. uint32_t group_id = family_to_group(rt_get_family(rc->rc_rt));
  318. if (nlmsg_get_group_writer(&nw, NLMSG_SMALL, NETLINK_ROUTE, group_id)) {
  319. struct route_nhop_data rnd = {
  320. .rnd_nhop = rc_get_nhop(rc),
  321. .rnd_weight = rc->rc_nh_weight,
  322. };
  323. hdr->nlmsg_flags &= ~(NLM_F_REPLACE | NLM_F_CREATE);
  324. hdr->nlmsg_flags &= ~(NLM_F_EXCL | NLM_F_APPEND);
  325. switch (rc->rc_cmd) {
  326. case RTM_ADD:
  327. hdr->nlmsg_type = NL_RTM_NEWROUTE;
  328. hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL;
  329. break;
  330. case RTM_CHANGE:
  331. hdr->nlmsg_type = NL_RTM_NEWROUTE;
  332. hdr->nlmsg_flags |= NLM_F_REPLACE;
  333. break;
  334. case RTM_DELETE:
  335. hdr->nlmsg_type = NL_RTM_DELROUTE;
  336. break;
  337. }
  338. dump_px(fibnum, hdr, rc->rc_rt, &rnd, &nw);
  339. nlmsg_flush(&nw);
  340. }
  341. rtsock_callback_p->route_f(fibnum, rc);
  342. }
  343. static void
  344. set_scope6(struct sockaddr *sa, struct ifnet *ifp)
  345. {
  346. #ifdef INET6
  347. if (sa != NULL && sa->sa_family == AF_INET6 && ifp != NULL) {
  348. struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;
  349. if (IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr))
  350. in6_set_unicast_scopeid(&sa6->sin6_addr, if_getindex(ifp));
  351. }
  352. #endif
  353. }
  354. struct rta_mpath_nh {
  355. struct sockaddr *gw;
  356. struct ifnet *ifp;
  357. uint8_t rtnh_flags;
  358. uint8_t rtnh_weight;
  359. };
  360. #define _IN(_field) offsetof(struct rtnexthop, _field)
  361. #define _OUT(_field) offsetof(struct rta_mpath_nh, _field)
  362. const static struct nlattr_parser nla_p_rtnh[] = {
  363. { .type = NL_RTA_GATEWAY, .off = _OUT(gw), .cb = nlattr_get_ip },
  364. { .type = NL_RTA_VIA, .off = _OUT(gw), .cb = nlattr_get_ipvia },
  365. };
  366. const static struct nlfield_parser nlf_p_rtnh[] = {
  367. { .off_in = _IN(rtnh_flags), .off_out = _OUT(rtnh_flags), .cb = nlf_get_u8 },
  368. { .off_in = _IN(rtnh_hops), .off_out = _OUT(rtnh_weight), .cb = nlf_get_u8 },
  369. { .off_in = _IN(rtnh_ifindex), .off_out = _OUT(ifp), .cb = nlf_get_ifpz },
  370. };
  371. #undef _IN
  372. #undef _OUT
  373. static bool
  374. post_p_rtnh(void *_attrs, struct nl_pstate *npt __unused)
  375. {
  376. struct rta_mpath_nh *attrs = (struct rta_mpath_nh *)_attrs;
  377. set_scope6(attrs->gw, attrs->ifp);
  378. return (true);
  379. }
  380. NL_DECLARE_PARSER_EXT(mpath_parser, struct rtnexthop, NULL, nlf_p_rtnh, nla_p_rtnh, post_p_rtnh);
  381. struct rta_mpath {
  382. int num_nhops;
  383. struct rta_mpath_nh nhops[0];
  384. };
  385. static int
  386. nlattr_get_multipath(struct nlattr *nla, struct nl_pstate *npt, const void *arg, void *target)
  387. {
  388. int data_len = nla->nla_len - sizeof(struct nlattr);
  389. struct rtnexthop *rtnh;
  390. int max_nhops = data_len / sizeof(struct rtnexthop);
  391. struct rta_mpath *mp = npt_alloc(npt, (max_nhops + 2) * sizeof(struct rta_mpath_nh));
  392. mp->num_nhops = 0;
  393. for (rtnh = (struct rtnexthop *)(nla + 1); data_len > 0; ) {
  394. struct rta_mpath_nh *mpnh = &mp->nhops[mp->num_nhops++];
  395. int error = nl_parse_header(rtnh, rtnh->rtnh_len, &mpath_parser,
  396. npt, mpnh);
  397. if (error != 0) {
  398. NLMSG_REPORT_ERR_MSG(npt, "RTA_MULTIPATH: nexhop %d: parse failed",
  399. mp->num_nhops - 1);
  400. return (error);
  401. }
  402. int len = NL_ITEM_ALIGN(rtnh->rtnh_len);
  403. data_len -= len;
  404. rtnh = (struct rtnexthop *)((char *)rtnh + len);
  405. }
  406. if (data_len != 0 || mp->num_nhops == 0) {
  407. NLMSG_REPORT_ERR_MSG(npt, "invalid RTA_MULTIPATH attr");
  408. return (EINVAL);
  409. }
  410. *((struct rta_mpath **)target) = mp;
  411. return (0);
  412. }
  413. struct nl_parsed_route {
  414. struct sockaddr *rta_dst;
  415. struct sockaddr *rta_gw;
  416. struct ifnet *rta_oif;
  417. struct rta_mpath *rta_multipath;
  418. uint32_t rta_table;
  419. uint32_t rta_rtflags;
  420. uint32_t rta_nh_id;
  421. uint32_t rta_weight;
  422. uint32_t rtax_mtu;
  423. uint8_t rtm_table;
  424. uint8_t rtm_family;
  425. uint8_t rtm_dst_len;
  426. uint8_t rtm_protocol;
  427. uint8_t rtm_type;
  428. uint32_t rtm_flags;
  429. };
  430. #define _IN(_field) offsetof(struct rtmsg, _field)
  431. #define _OUT(_field) offsetof(struct nl_parsed_route, _field)
  432. static struct nlattr_parser nla_p_rtmetrics[] = {
  433. { .type = NL_RTAX_MTU, .off = _OUT(rtax_mtu), .cb = nlattr_get_uint32 },
  434. };
  435. NL_DECLARE_ATTR_PARSER(metrics_parser, nla_p_rtmetrics);
  436. static const struct nlattr_parser nla_p_rtmsg[] = {
  437. { .type = NL_RTA_DST, .off = _OUT(rta_dst), .cb = nlattr_get_ip },
  438. { .type = NL_RTA_OIF, .off = _OUT(rta_oif), .cb = nlattr_get_ifp },
  439. { .type = NL_RTA_GATEWAY, .off = _OUT(rta_gw), .cb = nlattr_get_ip },
  440. { .type = NL_RTA_METRICS, .arg = &metrics_parser, .cb = nlattr_get_nested },
  441. { .type = NL_RTA_MULTIPATH, .off = _OUT(rta_multipath), .cb = nlattr_get_multipath },
  442. { .type = NL_RTA_WEIGHT, .off = _OUT(rta_weight), .cb = nlattr_get_uint32 },
  443. { .type = NL_RTA_RTFLAGS, .off = _OUT(rta_rtflags), .cb = nlattr_get_uint32 },
  444. { .type = NL_RTA_TABLE, .off = _OUT(rta_table), .cb = nlattr_get_uint32 },
  445. { .type = NL_RTA_VIA, .off = _OUT(rta_gw), .cb = nlattr_get_ipvia },
  446. { .type = NL_RTA_NH_ID, .off = _OUT(rta_nh_id), .cb = nlattr_get_uint32 },
  447. };
  448. static const struct nlfield_parser nlf_p_rtmsg[] = {
  449. { .off_in = _IN(rtm_family), .off_out = _OUT(rtm_family), .cb = nlf_get_u8 },
  450. { .off_in = _IN(rtm_dst_len), .off_out = _OUT(rtm_dst_len), .cb = nlf_get_u8 },
  451. { .off_in = _IN(rtm_protocol), .off_out = _OUT(rtm_protocol), .cb = nlf_get_u8 },
  452. { .off_in = _IN(rtm_type), .off_out = _OUT(rtm_type), .cb = nlf_get_u8 },
  453. { .off_in = _IN(rtm_table), .off_out = _OUT(rtm_table), .cb = nlf_get_u8 },
  454. { .off_in = _IN(rtm_flags), .off_out = _OUT(rtm_flags), .cb = nlf_get_u32 },
  455. };
  456. #undef _IN
  457. #undef _OUT
  458. static bool
  459. post_p_rtmsg(void *_attrs, struct nl_pstate *npt __unused)
  460. {
  461. struct nl_parsed_route *attrs = (struct nl_parsed_route *)_attrs;
  462. set_scope6(attrs->rta_dst, attrs->rta_oif);
  463. set_scope6(attrs->rta_gw, attrs->rta_oif);
  464. return (true);
  465. }
  466. NL_DECLARE_PARSER_EXT(rtm_parser, struct rtmsg, NULL, nlf_p_rtmsg, nla_p_rtmsg, post_p_rtmsg);
  467. struct netlink_walkargs {
  468. struct nl_writer *nw;
  469. struct route_nhop_data rnd;
  470. struct nlmsghdr hdr;
  471. struct nlpcb *nlp;
  472. uint32_t fibnum;
  473. int family;
  474. int error;
  475. int count;
  476. int dumped;
  477. int dumped_tables;
  478. };
  479. static int
  480. dump_rtentry(struct rtentry *rt, void *_arg)
  481. {
  482. struct netlink_walkargs *wa = (struct netlink_walkargs *)_arg;
  483. int error;
  484. wa->count++;
  485. if (wa->error != 0)
  486. return (0);
  487. if (!rt_is_exportable(rt, nlp_get_cred(wa->nlp)))
  488. return (0);
  489. wa->dumped++;
  490. rt_get_rnd(rt, &wa->rnd);
  491. error = dump_px(wa->fibnum, &wa->hdr, rt, &wa->rnd, wa->nw);
  492. IF_DEBUG_LEVEL(LOG_DEBUG3) {
  493. char rtbuf[INET6_ADDRSTRLEN + 5];
  494. FIB_LOG(LOG_DEBUG3, wa->fibnum, wa->family,
  495. "Dump %s, error %d",
  496. rt_print_buf(rt, rtbuf, sizeof(rtbuf)), error);
  497. }
  498. wa->error = error;
  499. return (0);
  500. }
  501. static void
  502. dump_rtable_one(struct netlink_walkargs *wa, uint32_t fibnum, int family)
  503. {
  504. FIB_LOG(LOG_DEBUG2, fibnum, family, "Start dump");
  505. wa->count = 0;
  506. wa->dumped = 0;
  507. rib_walk(fibnum, family, false, dump_rtentry, wa);
  508. wa->dumped_tables++;
  509. FIB_LOG(LOG_DEBUG2, fibnum, family, "End dump, iterated %d dumped %d",
  510. wa->count, wa->dumped);
  511. }
  512. static int
  513. dump_rtable_fib(struct netlink_walkargs *wa, uint32_t fibnum, int family)
  514. {
  515. wa->fibnum = fibnum;
  516. if (family == AF_UNSPEC) {
  517. for (int i = 0; i < AF_MAX; i++) {
  518. if (rt_tables_get_rnh(fibnum, i) != 0) {
  519. wa->family = i;
  520. dump_rtable_one(wa, fibnum, i);
  521. if (wa->error != 0)
  522. break;
  523. }
  524. }
  525. } else {
  526. if (rt_tables_get_rnh(fibnum, family) != 0) {
  527. wa->family = family;
  528. dump_rtable_one(wa, fibnum, family);
  529. }
  530. }
  531. return (wa->error);
  532. }
  533. static int
  534. handle_rtm_getroute(struct nlpcb *nlp, struct nl_parsed_route *attrs,
  535. struct nlmsghdr *hdr, struct nl_pstate *npt)
  536. {
  537. RIB_RLOCK_TRACKER;
  538. struct rib_head *rnh;
  539. const struct rtentry *rt;
  540. struct route_nhop_data rnd;
  541. uint32_t fibnum = attrs->rta_table;
  542. sa_family_t family = attrs->rtm_family;
  543. if (attrs->rta_dst == NULL) {
  544. NLMSG_REPORT_ERR_MSG(npt, "No RTA_DST supplied");
  545. return (EINVAL);
  546. }
  547. rnh = rt_tables_get_rnh(fibnum, family);
  548. if (rnh == NULL)
  549. return (EAFNOSUPPORT);
  550. RIB_RLOCK(rnh);
  551. struct sockaddr *dst = attrs->rta_dst;
  552. if (attrs->rtm_flags & RTM_F_PREFIX)
  553. rt = rib_lookup_prefix_plen(rnh, dst, attrs->rtm_dst_len, &rnd);
  554. else
  555. rt = (const struct rtentry *)rnh->rnh_matchaddr(dst, &rnh->head);
  556. if (rt == NULL) {
  557. RIB_RUNLOCK(rnh);
  558. return (ESRCH);
  559. }
  560. rt_get_rnd(rt, &rnd);
  561. rnd.rnd_nhop = nhop_select_func(rnd.rnd_nhop, 0);
  562. RIB_RUNLOCK(rnh);
  563. if (!rt_is_exportable(rt, nlp_get_cred(nlp)))
  564. return (ESRCH);
  565. IF_DEBUG_LEVEL(LOG_DEBUG2) {
  566. char rtbuf[NHOP_PRINT_BUFSIZE] __unused, nhbuf[NHOP_PRINT_BUFSIZE] __unused;
  567. FIB_LOG(LOG_DEBUG2, fibnum, family, "getroute completed: got %s for %s",
  568. nhop_print_buf_any(rnd.rnd_nhop, nhbuf, sizeof(nhbuf)),
  569. rt_print_buf(rt, rtbuf, sizeof(rtbuf)));
  570. }
  571. hdr->nlmsg_type = NL_RTM_NEWROUTE;
  572. dump_px(fibnum, hdr, rt, &rnd, npt->nw);
  573. return (0);
  574. }
  575. static int
  576. handle_rtm_dump(struct nlpcb *nlp, uint32_t fibnum, int family,
  577. struct nlmsghdr *hdr, struct nl_writer *nw)
  578. {
  579. struct netlink_walkargs wa = {
  580. .nlp = nlp,
  581. .nw = nw,
  582. .hdr.nlmsg_pid = hdr->nlmsg_pid,
  583. .hdr.nlmsg_seq = hdr->nlmsg_seq,
  584. .hdr.nlmsg_type = NL_RTM_NEWROUTE,
  585. .hdr.nlmsg_flags = hdr->nlmsg_flags | NLM_F_MULTI,
  586. };
  587. if (fibnum == RT_TABLE_UNSPEC) {
  588. for (int i = 0; i < V_rt_numfibs; i++) {
  589. dump_rtable_fib(&wa, fibnum, family);
  590. if (wa.error != 0)
  591. break;
  592. }
  593. } else
  594. dump_rtable_fib(&wa, fibnum, family);
  595. if (wa.error == 0 && wa.dumped_tables == 0) {
  596. FIB_LOG(LOG_DEBUG, fibnum, family, "incorrect fibnum/family");
  597. wa.error = ESRCH;
  598. // How do we propagate it?
  599. }
  600. if (!nlmsg_end_dump(wa.nw, wa.error, &wa.hdr)) {
  601. NL_LOG(LOG_DEBUG, "Unable to finalize the dump");
  602. return (ENOMEM);
  603. }
  604. return (wa.error);
  605. }
  606. static struct nhop_object *
  607. finalize_nhop(struct nhop_object *nh, const struct sockaddr *dst, int *perror)
  608. {
  609. /*
  610. * The following MUST be filled:
  611. * nh_ifp, nh_ifa, nh_gw
  612. */
  613. if (nh->gw_sa.sa_family == 0) {
  614. /*
  615. * Empty gateway. Can be direct route with RTA_OIF set.
  616. */
  617. if (nh->nh_ifp != NULL)
  618. nhop_set_direct_gw(nh, nh->nh_ifp);
  619. else {
  620. NL_LOG(LOG_DEBUG, "empty gateway and interface, skipping");
  621. *perror = EINVAL;
  622. return (NULL);
  623. }
  624. /* Both nh_ifp and gateway are set */
  625. } else {
  626. /* Gateway is set up, we can derive ifp if not set */
  627. if (nh->nh_ifp == NULL) {
  628. uint32_t fibnum = nhop_get_fibnum(nh);
  629. uint32_t flags = 0;
  630. if (nh->nh_flags & NHF_GATEWAY)
  631. flags = RTF_GATEWAY;
  632. else if (nh->nh_flags & NHF_HOST)
  633. flags = RTF_HOST;
  634. struct ifaddr *ifa = ifa_ifwithroute(flags, dst, &nh->gw_sa, fibnum);
  635. if (ifa == NULL) {
  636. NL_LOG(LOG_DEBUG, "Unable to determine ifp, skipping");
  637. *perror = EINVAL;
  638. return (NULL);
  639. }
  640. nhop_set_transmit_ifp(nh, ifa->ifa_ifp);
  641. }
  642. }
  643. /* Both nh_ifp and gateway are set */
  644. if (nh->nh_ifa == NULL) {
  645. const struct sockaddr *gw_sa = &nh->gw_sa;
  646. if (gw_sa->sa_family != dst->sa_family) {
  647. /*
  648. * Use dst as the target for determining the default
  649. * preferred ifa IF
  650. * 1) the gateway is link-level (e.g. direct route)
  651. * 2) the gateway family is different (e.g. IPv4 over IPv6).
  652. */
  653. gw_sa = dst;
  654. }
  655. struct ifaddr *ifa = ifaof_ifpforaddr(gw_sa, nh->nh_ifp);
  656. if (ifa == NULL) {
  657. /* Try link-level ifa. */
  658. gw_sa = &nh->gw_sa;
  659. ifa = ifaof_ifpforaddr(gw_sa, nh->nh_ifp);
  660. if (ifa == NULL) {
  661. NL_LOG(LOG_DEBUG, "Unable to determine ifa, skipping");
  662. *perror = EINVAL;
  663. return (NULL);
  664. }
  665. }
  666. nhop_set_src(nh, ifa);
  667. }
  668. return (nhop_get_nhop(nh, perror));
  669. }
  670. static int
  671. get_pxflag(const struct nl_parsed_route *attrs)
  672. {
  673. int pxflag = 0;
  674. switch (attrs->rtm_family) {
  675. case AF_INET:
  676. if (attrs->rtm_dst_len == 32)
  677. pxflag = NHF_HOST;
  678. else if (attrs->rtm_dst_len == 0)
  679. pxflag = NHF_DEFAULT;
  680. break;
  681. case AF_INET6:
  682. if (attrs->rtm_dst_len == 128)
  683. pxflag = NHF_HOST;
  684. else if (attrs->rtm_dst_len == 0)
  685. pxflag = NHF_DEFAULT;
  686. break;
  687. }
  688. return (pxflag);
  689. }
  690. static int
  691. get_op_flags(int nlm_flags)
  692. {
  693. int op_flags = 0;
  694. op_flags |= (nlm_flags & NLM_F_REPLACE) ? RTM_F_REPLACE : 0;
  695. op_flags |= (nlm_flags & NLM_F_EXCL) ? RTM_F_EXCL : 0;
  696. op_flags |= (nlm_flags & NLM_F_CREATE) ? RTM_F_CREATE : 0;
  697. op_flags |= (nlm_flags & NLM_F_APPEND) ? RTM_F_APPEND : 0;
  698. return (op_flags);
  699. }
  700. #ifdef ROUTE_MPATH
  701. static int
  702. create_nexthop_one(struct nl_parsed_route *attrs, struct rta_mpath_nh *mpnh,
  703. struct nl_pstate *npt, struct nhop_object **pnh)
  704. {
  705. int error;
  706. if (mpnh->gw == NULL)
  707. return (EINVAL);
  708. struct nhop_object *nh = nhop_alloc(attrs->rta_table, attrs->rtm_family);
  709. if (nh == NULL)
  710. return (ENOMEM);
  711. error = nl_set_nexthop_gw(nh, mpnh->gw, mpnh->ifp, npt);
  712. if (error != 0) {
  713. nhop_free(nh);
  714. return (error);
  715. }
  716. if (mpnh->ifp != NULL)
  717. nhop_set_transmit_ifp(nh, mpnh->ifp);
  718. nhop_set_pxtype_flag(nh, get_pxflag(attrs));
  719. nhop_set_rtflags(nh, attrs->rta_rtflags);
  720. if (attrs->rtm_protocol > RTPROT_STATIC)
  721. nhop_set_origin(nh, attrs->rtm_protocol);
  722. *pnh = finalize_nhop(nh, attrs->rta_dst, &error);
  723. return (error);
  724. }
  725. #endif
  726. static struct nhop_object *
  727. create_nexthop_from_attrs(struct nl_parsed_route *attrs,
  728. struct nl_pstate *npt, int *perror)
  729. {
  730. struct nhop_object *nh = NULL;
  731. int error = 0;
  732. if (attrs->rta_multipath != NULL) {
  733. #ifdef ROUTE_MPATH
  734. /* Multipath w/o explicit nexthops */
  735. int num_nhops = attrs->rta_multipath->num_nhops;
  736. struct weightened_nhop *wn = npt_alloc(npt, sizeof(*wn) * num_nhops);
  737. for (int i = 0; i < num_nhops; i++) {
  738. struct rta_mpath_nh *mpnh = &attrs->rta_multipath->nhops[i];
  739. error = create_nexthop_one(attrs, mpnh, npt, &wn[i].nh);
  740. if (error != 0) {
  741. for (int j = 0; j < i; j++)
  742. nhop_free(wn[j].nh);
  743. break;
  744. }
  745. wn[i].weight = mpnh->rtnh_weight > 0 ? mpnh->rtnh_weight : 1;
  746. }
  747. if (error == 0) {
  748. struct rib_head *rh = nhop_get_rh(wn[0].nh);
  749. struct nhgrp_object *nhg;
  750. nhg = nhgrp_alloc(rh->rib_fibnum, rh->rib_family,
  751. wn, num_nhops, perror);
  752. if (nhg != NULL) {
  753. if (attrs->rtm_protocol > RTPROT_STATIC)
  754. nhgrp_set_origin(nhg, attrs->rtm_protocol);
  755. nhg = nhgrp_get_nhgrp(nhg, perror);
  756. }
  757. for (int i = 0; i < num_nhops; i++)
  758. nhop_free(wn[i].nh);
  759. if (nhg != NULL)
  760. return ((struct nhop_object *)nhg);
  761. error = *perror;
  762. }
  763. #else
  764. error = ENOTSUP;
  765. #endif
  766. *perror = error;
  767. } else {
  768. nh = nhop_alloc(attrs->rta_table, attrs->rtm_family);
  769. if (nh == NULL) {
  770. *perror = ENOMEM;
  771. return (NULL);
  772. }
  773. if (attrs->rta_gw != NULL) {
  774. *perror = nl_set_nexthop_gw(nh, attrs->rta_gw, attrs->rta_oif, npt);
  775. if (*perror != 0) {
  776. nhop_free(nh);
  777. return (NULL);
  778. }
  779. }
  780. if (attrs->rta_oif != NULL)
  781. nhop_set_transmit_ifp(nh, attrs->rta_oif);
  782. if (attrs->rtax_mtu != 0)
  783. nhop_set_mtu(nh, attrs->rtax_mtu, true);
  784. if (attrs->rta_rtflags & RTF_BROADCAST)
  785. nhop_set_broadcast(nh, true);
  786. if (attrs->rtm_protocol > RTPROT_STATIC)
  787. nhop_set_origin(nh, attrs->rtm_protocol);
  788. nhop_set_pxtype_flag(nh, get_pxflag(attrs));
  789. nhop_set_rtflags(nh, attrs->rta_rtflags);
  790. switch (attrs->rtm_type) {
  791. case RTN_UNICAST:
  792. break;
  793. case RTN_BLACKHOLE:
  794. nhop_set_blackhole(nh, RTF_BLACKHOLE);
  795. break;
  796. case RTN_PROHIBIT:
  797. case RTN_UNREACHABLE:
  798. nhop_set_blackhole(nh, RTF_REJECT);
  799. break;
  800. /* TODO: return ENOTSUP for other types if strict option is set */
  801. }
  802. nh = finalize_nhop(nh, attrs->rta_dst, perror);
  803. }
  804. return (nh);
  805. }
  806. static int
  807. rtnl_handle_newroute(struct nlmsghdr *hdr, struct nlpcb *nlp,
  808. struct nl_pstate *npt)
  809. {
  810. struct rib_cmd_info rc = {};
  811. struct nhop_object *nh = NULL;
  812. int error;
  813. struct nl_parsed_route attrs = {};
  814. error = nl_parse_nlmsg(hdr, &rtm_parser, npt, &attrs);
  815. if (error != 0)
  816. return (error);
  817. /* Check if we have enough data */
  818. if (attrs.rta_dst == NULL) {
  819. NL_LOG(LOG_DEBUG, "missing RTA_DST");
  820. return (EINVAL);
  821. }
  822. if (attrs.rtm_table > 0 && attrs.rta_table == 0) {
  823. /* pre-2.6.19 Linux API compatibility */
  824. attrs.rta_table = attrs.rtm_table;
  825. } else if (attrs.rta_table >= V_rt_numfibs) {
  826. NLMSG_REPORT_ERR_MSG(npt, "invalid fib");
  827. return (EINVAL);
  828. }
  829. if (attrs.rta_nh_id != 0) {
  830. /* Referenced uindex */
  831. int pxflag = get_pxflag(&attrs);
  832. nh = nl_find_nhop(attrs.rta_table, attrs.rtm_family, attrs.rta_nh_id,
  833. pxflag, &error);
  834. if (error != 0)
  835. return (error);
  836. } else {
  837. nh = create_nexthop_from_attrs(&attrs, npt, &error);
  838. if (error != 0) {
  839. NL_LOG(LOG_DEBUG, "Error creating nexthop");
  840. return (error);
  841. }
  842. }
  843. if (!NH_IS_NHGRP(nh) && attrs.rta_weight == 0)
  844. attrs.rta_weight = RT_DEFAULT_WEIGHT;
  845. struct route_nhop_data rnd = { .rnd_nhop = nh, .rnd_weight = attrs.rta_weight };
  846. int op_flags = get_op_flags(hdr->nlmsg_flags);
  847. error = rib_add_route_px(attrs.rta_table, attrs.rta_dst, attrs.rtm_dst_len,
  848. &rnd, op_flags, &rc);
  849. if (error == 0)
  850. report_operation(attrs.rta_table, &rc, nlp, hdr);
  851. return (error);
  852. }
  853. static int
  854. path_match_func(const struct rtentry *rt, const struct nhop_object *nh, void *_data)
  855. {
  856. struct nl_parsed_route *attrs = (struct nl_parsed_route *)_data;
  857. if ((attrs->rta_gw != NULL) && !rib_match_gw(rt, nh, attrs->rta_gw))
  858. return (0);
  859. if ((attrs->rta_oif != NULL) && (attrs->rta_oif != nh->nh_ifp))
  860. return (0);
  861. return (1);
  862. }
  863. static int
  864. rtnl_handle_delroute(struct nlmsghdr *hdr, struct nlpcb *nlp,
  865. struct nl_pstate *npt)
  866. {
  867. struct rib_cmd_info rc;
  868. int error;
  869. struct nl_parsed_route attrs = {};
  870. error = nl_parse_nlmsg(hdr, &rtm_parser, npt, &attrs);
  871. if (error != 0)
  872. return (error);
  873. if (attrs.rta_dst == NULL) {
  874. NLMSG_REPORT_ERR_MSG(npt, "RTA_DST is not set");
  875. return (ESRCH);
  876. }
  877. if (attrs.rta_table >= V_rt_numfibs) {
  878. NLMSG_REPORT_ERR_MSG(npt, "invalid fib");
  879. return (EINVAL);
  880. }
  881. error = rib_del_route_px(attrs.rta_table, attrs.rta_dst,
  882. attrs.rtm_dst_len, path_match_func, &attrs, 0, &rc);
  883. if (error == 0)
  884. report_operation(attrs.rta_table, &rc, nlp, hdr);
  885. return (error);
  886. }
  887. static int
  888. rtnl_handle_getroute(struct nlmsghdr *hdr, struct nlpcb *nlp, struct nl_pstate *npt)
  889. {
  890. int error;
  891. struct nl_parsed_route attrs = {};
  892. error = nl_parse_nlmsg(hdr, &rtm_parser, npt, &attrs);
  893. if (error != 0)
  894. return (error);
  895. if (attrs.rta_table >= V_rt_numfibs) {
  896. NLMSG_REPORT_ERR_MSG(npt, "invalid fib");
  897. return (EINVAL);
  898. }
  899. if (hdr->nlmsg_flags & NLM_F_DUMP)
  900. error = handle_rtm_dump(nlp, attrs.rta_table, attrs.rtm_family, hdr, npt->nw);
  901. else
  902. error = handle_rtm_getroute(nlp, &attrs, hdr, npt);
  903. return (error);
  904. }
  905. void
  906. rtnl_handle_route_event(uint32_t fibnum, const struct rib_cmd_info *rc)
  907. {
  908. struct nl_writer nw = {};
  909. int family, nlm_flags = 0;
  910. family = rt_get_family(rc->rc_rt);
  911. /* XXX: check if there are active listeners first */
  912. /* TODO: consider passing PID/type/seq */
  913. switch (rc->rc_cmd) {
  914. case RTM_ADD:
  915. nlm_flags = NLM_F_EXCL | NLM_F_CREATE;
  916. break;
  917. case RTM_CHANGE:
  918. nlm_flags = NLM_F_REPLACE;
  919. break;
  920. case RTM_DELETE:
  921. nlm_flags = 0;
  922. break;
  923. }
  924. IF_DEBUG_LEVEL(LOG_DEBUG2) {
  925. char rtbuf[NHOP_PRINT_BUFSIZE] __unused;
  926. FIB_LOG(LOG_DEBUG2, fibnum, family,
  927. "received event %s for %s / nlm_flags=%X",
  928. rib_print_cmd(rc->rc_cmd),
  929. rt_print_buf(rc->rc_rt, rtbuf, sizeof(rtbuf)),
  930. nlm_flags);
  931. }
  932. struct nlmsghdr hdr = {
  933. .nlmsg_flags = nlm_flags,
  934. .nlmsg_type = get_rtmsg_type_from_rtsock(rc->rc_cmd),
  935. };
  936. struct route_nhop_data rnd = {
  937. .rnd_nhop = rc_get_nhop(rc),
  938. .rnd_weight = rc->rc_nh_weight,
  939. };
  940. uint32_t group_id = family_to_group(family);
  941. if (!nlmsg_get_group_writer(&nw, NLMSG_SMALL, NETLINK_ROUTE, group_id)) {
  942. NL_LOG(LOG_DEBUG, "error allocating event buffer");
  943. return;
  944. }
  945. dump_px(fibnum, &hdr, rc->rc_rt, &rnd, &nw);
  946. nlmsg_flush(&nw);
  947. }
  948. static const struct rtnl_cmd_handler cmd_handlers[] = {
  949. {
  950. .cmd = NL_RTM_GETROUTE,
  951. .name = "RTM_GETROUTE",
  952. .cb = &rtnl_handle_getroute,
  953. .flags = RTNL_F_ALLOW_NONVNET_JAIL,
  954. },
  955. {
  956. .cmd = NL_RTM_DELROUTE,
  957. .name = "RTM_DELROUTE",
  958. .cb = &rtnl_handle_delroute,
  959. .priv = PRIV_NET_ROUTE,
  960. },
  961. {
  962. .cmd = NL_RTM_NEWROUTE,
  963. .name = "RTM_NEWROUTE",
  964. .cb = &rtnl_handle_newroute,
  965. .priv = PRIV_NET_ROUTE,
  966. }
  967. };
  968. static const struct nlhdr_parser *all_parsers[] = {&mpath_parser, &metrics_parser, &rtm_parser};
  969. void
  970. rtnl_routes_init(void)
  971. {
  972. NL_VERIFY_PARSERS(all_parsers);
  973. rtnl_register_messages(cmd_handlers, NL_ARRAY_LEN(cmd_handlers));
  974. }