📄 netlink.c
字号:
case RTD_PROHIBIT: break; default: return 0; } return 1;}static voidnl_send_route(struct krt_proto *p, rte *e, int new){ net *net = e->net; rta *a = e->attrs; struct { struct nlmsghdr h; struct rtmsg r; char buf[128]; } r; DBG("nl_send_route(%I/%d,new=%d)\n", net->n.prefix, net->n.pxlen, new); bzero(&r.h, sizeof(r.h)); bzero(&r.r, sizeof(r.r)); r.h.nlmsg_type = new ? RTM_NEWROUTE : RTM_DELROUTE; r.h.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); r.h.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | (new ? NLM_F_CREATE|NLM_F_REPLACE : 0); r.r.rtm_family = BIRD_AF; r.r.rtm_dst_len = net->n.pxlen; r.r.rtm_tos = 0; r.r.rtm_table = KRT_CF->scan.table_id; r.r.rtm_protocol = RTPROT_BIRD; r.r.rtm_scope = RT_SCOPE_UNIVERSE; nl_add_attr_ipa(&r.h, sizeof(r), RTA_DST, net->n.prefix); switch (a->dest) { case RTD_ROUTER: r.r.rtm_type = RTN_UNICAST; nl_add_attr_ipa(&r.h, sizeof(r), RTA_GATEWAY, a->gw); break; case RTD_DEVICE: if (!a->iface) return; r.r.rtm_type = RTN_UNICAST; nl_add_attr_u32(&r.h, sizeof(r), RTA_OIF, a->iface->index); break; case RTD_BLACKHOLE: r.r.rtm_type = RTN_BLACKHOLE; break; case RTD_UNREACHABLE: r.r.rtm_type = RTN_UNREACHABLE; break; case RTD_PROHIBIT: r.r.rtm_type = RTN_PROHIBIT; break; default: bug("krt_capable inconsistent with nl_send_route"); } nl_exchange(&r.h);}voidkrt_set_notify(struct krt_proto *p, net *n UNUSED, rte *new, rte *old){ if (old && new) { /* * We should check whether priority and TOS is identical as well, * but we don't use these and default value is always equal to default value. :-) */ nl_send_route(p, new, 1); } else { if (old) nl_send_route(p, old, 0); if (new) nl_send_route(p, new, 1); }}static struct iface *krt_temp_iface(struct krt_proto *p, unsigned index){ struct iface *i, *j; WALK_LIST(i, p->scan.temp_ifs) if (i->index == index) return i; i = mb_allocz(p->p.pool, sizeof(struct iface)); if (j = if_find_by_index(index)) strcpy(i->name, j->name); else strcpy(i->name, "?"); i->index = index; add_tail(&p->scan.temp_ifs, &i->n); return i;}static voidnl_parse_route(struct nlmsghdr *h, int scan){ struct krt_proto *p; struct rtmsg *i; struct rtattr *a[RTA_CACHEINFO+1]; int new = h->nlmsg_type == RTM_NEWROUTE; ip_addr dst; rta ra; rte *e; net *net; u32 oif; int src; if (!(i = nl_checkin(h, sizeof(*i))) || !nl_parse_attrs(RTM_RTA(i), a, sizeof(a))) return; if (i->rtm_family != BIRD_AF) return; if ((a[RTA_DST] && RTA_PAYLOAD(a[RTA_DST]) != sizeof(ip_addr)) || (a[RTA_OIF] && RTA_PAYLOAD(a[RTA_OIF]) != 4) || (a[RTA_PRIORITY] && RTA_PAYLOAD(a[RTA_PRIORITY]) != 4) ||#ifdef IPV6 (a[RTA_IIF] && RTA_PAYLOAD(a[RTA_IIF]) != 4) ||#endif (a[RTA_GATEWAY] && RTA_PAYLOAD(a[RTA_GATEWAY]) != sizeof(ip_addr))) { log(L_ERR "nl_parse_route: Malformed message received"); return; } p = nl_table_map[i->rtm_table]; /* Do we know this table? */ if (!p) return;#ifdef IPV6 if (a[RTA_IIF]) { DBG("KRT: Ignoring route with IIF set\n"); return; }#else if (i->rtm_tos != 0) /* We don't support TOS */ { DBG("KRT: Ignoring route with TOS %02x\n", i->rtm_tos); return; }#endif if (scan && !new) { DBG("KRT: Ignoring route deletion\n"); return; } if (a[RTA_DST]) { memcpy(&dst, RTA_DATA(a[RTA_DST]), sizeof(dst)); ipa_ntoh(dst); } else dst = IPA_NONE; if (a[RTA_OIF]) memcpy(&oif, RTA_DATA(a[RTA_OIF]), sizeof(oif)); else oif = ~0; DBG("Got %I/%d, type=%d, oif=%d, table=%d, prid=%d, proto=%s\n", dst, i->rtm_dst_len, i->rtm_type, oif, i->rtm_table, i->rtm_protocol, p->p.name); switch (i->rtm_protocol) { case RTPROT_REDIRECT: src = KRT_SRC_REDIRECT; break; case RTPROT_KERNEL: DBG("Route originated in kernel, ignoring\n"); return; case RTPROT_BIRD:#ifdef IPV6 case RTPROT_BOOT: /* Current Linux kernels don't remember rtm_protocol for IPv6 routes and supply RTPROT_BOOT instead */#endif if (!scan) { DBG("Echo of our own route, ignoring\n"); return; } src = KRT_SRC_BIRD; break; default: src = KRT_SRC_ALIEN; } net = net_get(p->p.table, dst, i->rtm_dst_len); ra.proto = &p->p; ra.source = RTS_INHERIT; ra.scope = SCOPE_UNIVERSE; ra.cast = RTC_UNICAST; ra.flags = ra.aflags = 0; ra.from = IPA_NONE; ra.gw = IPA_NONE; ra.iface = NULL; ra.eattrs = NULL; switch (i->rtm_type) { case RTN_UNICAST: if (oif == ~0U) { log(L_ERR "KRT: Mysterious route with no OIF (%I/%d)", net->n.prefix, net->n.pxlen); return; } if (a[RTA_GATEWAY]) { neighbor *ng; ra.dest = RTD_ROUTER; memcpy(&ra.gw, RTA_DATA(a[RTA_GATEWAY]), sizeof(ra.gw)); ipa_ntoh(ra.gw); ng = neigh_find(&p->p, &ra.gw, 0); if (ng && ng->scope) ra.iface = ng->iface; else /* FIXME: Remove this warning? Handle it somehow... */ log(L_WARN "Kernel told us to use non-neighbor %I for %I/%d", ra.gw, net->n.prefix, net->n.pxlen); } else { ra.dest = RTD_DEVICE; ra.iface = krt_temp_iface(p, oif); } break; case RTN_BLACKHOLE: ra.dest = RTD_BLACKHOLE; break; case RTN_UNREACHABLE: ra.dest = RTD_UNREACHABLE; break; case RTN_PROHIBIT: ra.dest = RTD_PROHIBIT; break; /* FIXME: What about RTN_THROW? */ default: DBG("KRT: Ignoring route with type=%d\n", i->rtm_type); return; } if (i->rtm_scope != RT_SCOPE_UNIVERSE) { DBG("KRT: Ignoring route with scope=%d\n", i->rtm_scope); return; } e = rte_get_temp(&ra); e->net = net; e->u.krt.src = src; e->u.krt.proto = i->rtm_protocol; e->u.krt.type = i->rtm_type; if (a[RTA_PRIORITY]) memcpy(&e->u.krt.metric, RTA_DATA(a[RTA_PRIORITY]), sizeof(e->u.krt.metric)); else e->u.krt.metric = 0; if (scan) krt_got_route(p, e); else krt_got_route_async(p, e, new);}voidkrt_scan_fire(struct krt_proto *p UNUSED) /* CONFIG_ALL_TABLES_AT_ONCE => p is NULL */{ struct nlmsghdr *h; nl_request_dump(RTM_GETROUTE); while (h = nl_get_scan()) if (h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE) nl_parse_route(h, 1); else log(L_DEBUG "nl_scan_fire: Unknown packet received (type=%d)", h->nlmsg_type);}/* * Asynchronous Netlink interface */static sock *nl_async_sk; /* BIRD socket for asynchronous notifications */static byte *nl_async_rx_buffer; /* Receive buffer */static voidnl_async_msg(struct nlmsghdr *h){ switch (h->nlmsg_type) { case RTM_NEWROUTE: case RTM_DELROUTE: DBG("KRT: Received async route notification (%d)\n", h->nlmsg_type); nl_parse_route(h, 0); break; case RTM_NEWLINK: case RTM_DELLINK: DBG("KRT: Received async link notification (%d)\n", h->nlmsg_type); nl_parse_link(h, 0); break; case RTM_NEWADDR: case RTM_DELADDR: DBG("KRT: Received async address notification (%d)\n", h->nlmsg_type); nl_parse_addr(h); break; default: DBG("KRT: Received unknown async notification (%d)\n", h->nlmsg_type); }}static intnl_async_hook(sock *sk, int size UNUSED){ struct iovec iov = { nl_async_rx_buffer, NL_RX_SIZE }; struct sockaddr_nl sa; struct msghdr m = { (struct sockaddr *) &sa, sizeof(sa), &iov, 1, NULL, 0, 0 }; struct nlmsghdr *h; int x; unsigned int len; nl_last_hdr = NULL; /* Discard packets accidentally remaining in the rxbuf */ x = recvmsg(sk->fd, &m, 0); if (x < 0) { if (errno == ENOBUFS) { /* * Netlink reports some packets have been thrown away. * One day we might react to it by asking for route table * scan in near future. */ return 1; /* More data are likely to be ready */ } else if (errno != EWOULDBLOCK) log(L_ERR "Netlink recvmsg: %m"); return 0; } if (sa.nl_pid) /* It isn't from the kernel */ { DBG("Non-kernel packet\n"); return 1; } h = (void *) nl_async_rx_buffer; len = x; if (m.msg_flags & MSG_TRUNC) { log(L_WARN "Netlink got truncated asynchronous message"); return 1; } while (NLMSG_OK(h, len)) { nl_async_msg(h); h = NLMSG_NEXT(h, len); } if (len) log(L_WARN "nl_async_hook: Found packet remnant of size %d", len); return 1;}static voidnl_open_async(void){ sock *sk; struct sockaddr_nl sa; int fd; static int nl_open_tried = 0; if (nl_open_tried) return; nl_open_tried = 1; DBG("KRT: Opening async netlink socket\n"); fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (fd < 0) { log(L_ERR "Unable to open asynchronous rtnetlink socket: %m"); return; } bzero(&sa, sizeof(sa)); sa.nl_family = AF_NETLINK;#ifdef IPV6 sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV6_IFADDR | RTMGRP_IPV6_ROUTE;#else sa.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV4_ROUTE;#endif if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { log(L_ERR "Unable to bind asynchronous rtnetlink socket: %m"); return; } sk = nl_async_sk = sk_new(krt_pool); sk->type = SK_MAGIC; sk->rx_hook = nl_async_hook; sk->fd = fd; if (sk_open(sk)) bug("Netlink: sk_open failed"); if (!nl_async_rx_buffer) nl_async_rx_buffer = xmalloc(NL_RX_SIZE);}/* * Interface to the UNIX krt module */static u8 nl_cf_table[(NL_NUM_TABLES+7) / 8];voidkrt_scan_preconfig(struct config *c UNUSED){ bzero(&nl_cf_table, sizeof(nl_cf_table));}voidkrt_scan_postconfig(struct krt_config *x){ int id = x->scan.table_id; if (nl_cf_table[id/8] & (1 << (id%8))) cf_error("Multiple kernel syncers defined for table #%d", id); nl_cf_table[id/8] |= (1 << (id%8));}voidkrt_scan_construct(struct krt_config *x){#ifndef IPV6 x->scan.table_id = RT_TABLE_MAIN;#else x->scan.table_id = 254;#endif}voidkrt_scan_start(struct krt_proto *p, int first){ init_list(&p->scan.temp_ifs); nl_table_map[KRT_CF->scan.table_id] = p; if (first) { nl_open(); nl_open_async(); }}voidkrt_scan_shutdown(struct krt_proto *p UNUSED, int last UNUSED){}voidkrt_if_start(struct kif_proto *p UNUSED){ nl_open(); nl_open_async();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -