void kernel_netlink_init(void) { unsigned long groups; /* Start with a netlink address lookup */ netlink_address_lookup(); /* * Prepare netlink kernel broadcast channel * subscribtion. We subscribe to LINK and ADDR * netlink broadcast messages. */ groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR; netlink_socket(&nl_kernel, groups); if (nl_kernel.fd > 0) { log_message(LOG_INFO, "Registering Kernel netlink reflector"); thread_add_read(master, kernel_netlink, NULL, nl_kernel.fd, NETLINK_TIMER); } else log_message(LOG_INFO, "Error while registering Kernel netlink reflector channel"); /* Prepare netlink command channel. */ netlink_socket(&nl_cmd, 0); if (nl_cmd.fd > 0) log_message(LOG_INFO, "Registering Kernel netlink command channel"); else log_message(LOG_INFO, "Error while registering Kernel netlink cmd channel"); }
void kernel_netlink_init(void) { /* Start with a netlink address lookup */ netlink_address_lookup(); /* * Prepare netlink kernel broadcast channel * subscribtion. We subscribe to LINK and ADDR * netlink broadcast messages. */ netlink_socket(&nl_kernel, SOCK_NONBLOCK, RTNLGRP_LINK, RTNLGRP_IPV4_IFADDR, RTNLGRP_IPV6_IFADDR, 0); if (nl_kernel.fd > 0) { log_message(LOG_INFO, "Registering Kernel netlink reflector"); nl_kernel.thread = thread_add_read(master, kernel_netlink, &nl_kernel, nl_kernel.fd, NETLINK_TIMER); } else log_message(LOG_INFO, "Error while registering Kernel netlink reflector channel"); /* Prepare netlink command channel. */ netlink_socket(&nl_cmd, SOCK_NONBLOCK, 0); if (nl_cmd.fd > 0) log_message(LOG_INFO, "Registering Kernel netlink command channel"); else log_message(LOG_INFO, "Error while registering Kernel netlink cmd channel"); }
/* Filter out messages from self that occur on listener socket, caused by our actions on the command socket */ static void netlink_install_filter (int sock, __u32 pid) { struct sock_filter filter[] = { /* 0: ldh [4] */ BPF_STMT(BPF_LD|BPF_ABS|BPF_H, offsetof(struct nlmsghdr, nlmsg_type)), /* 1: jeq 0x18 jt 3 jf 6 */ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_NEWROUTE), 1, 0), /* 2: jeq 0x19 jt 3 jf 6 */ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_DELROUTE), 0, 3), /* 3: ldw [12] */ BPF_STMT(BPF_LD|BPF_ABS|BPF_W, offsetof(struct nlmsghdr, nlmsg_pid)), /* 4: jeq XX jt 5 jf 6 */ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htonl(pid), 0, 1), /* 5: ret 0 (skip) */ BPF_STMT(BPF_RET|BPF_K, 0), /* 6: ret 0xffff (keep) */ BPF_STMT(BPF_RET|BPF_K, 0xffff), }; struct sock_fprog prog = { .len = sizeof(filter) / sizeof(filter[0]), .filter = filter, }; if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog)) < 0) zlog_warn ("Can't install socket filter: %s\n", safe_strerror(errno)); } /* Exported interface function. This function simply calls netlink_socket (). */ void kernel_init (void) { unsigned long groups; groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR; #ifdef HAVE_IPV6 groups |= RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR; #endif /* HAVE_IPV6 */ netlink_socket (&netlink, groups); netlink_socket (&netlink_cmd, 0); /* Register kernel socket. */ if (netlink.sock > 0) { /* Only want non-blocking on the netlink event socket */ if (fcntl (netlink.sock, F_SETFL, O_NONBLOCK) < 0) zlog (NULL, LOG_ERR, "Can't set %s socket flags: %s", netlink.name, safe_strerror (errno)); /* Set receive buffer size if it's set from command line */ if (nl_rcvbufsize) netlink_recvbuf (&netlink, nl_rcvbufsize); netlink_install_filter (netlink.sock, netlink_cmd.snl.nl_pid); thread_add_read (hm->master, kernel_read, NULL, netlink.sock); } }
/* Exported interface function. This function simply calls netlink_socket (). */ void kernel_init () { unsigned long groups; groups = RTMGRP_LINK|RTMGRP_IPV4_ROUTE|RTMGRP_IPV4_IFADDR; #ifdef HAVE_IPV6 groups |= RTMGRP_IPV6_ROUTE|RTMGRP_IPV6_IFADDR; #endif /* HAVE_IPV6 */ netlink_socket (&netlink, groups); netlink_socket (&netlink_cmd, 0); /* Register kernel socket. */ if (netlink.sock > 0) thread_add_read (master, kernel_read, NULL, netlink.sock); }
/* Interfaces lookup bootstrap function */ int netlink_interface_lookup(void) { nl_handle_t nlh; int status = 0; int ret, flags; if (netlink_socket(&nlh, 0) < 0) return -1; /* Set blocking flag */ ret = netlink_set_block(&nlh, &flags); if (ret < 0) log_message(LOG_INFO, "Netlink: Warning, couldn't set " "blocking flag to netlink socket..."); /* Interface lookup */ if (netlink_request(&nlh, AF_PACKET, RTM_GETLINK) < 0) { status = -1; goto end_int; } status = netlink_parse_info(netlink_if_link_filter, &nlh, NULL); end_int: netlink_close(&nlh); return status; }
/* Adresses lookup bootstrap function */ static int netlink_address_lookup(void) { nl_handle_t nlh; int status = 0; int ret, flags; if (netlink_socket(&nlh, 0) < 0) return -1; /* Set blocking flag */ ret = netlink_set_block(&nlh, &flags); if (ret < 0) log_message(LOG_INFO, "Netlink: Warning, couldn't set " "blocking flag to netlink socket..."); /* IPv4 Address lookup */ if (netlink_request(&nlh, AF_INET, RTM_GETADDR) < 0) { status = -1; goto end_addr; } status = netlink_parse_info(netlink_if_address_filter, &nlh, NULL); /* IPv6 Address lookup */ if (netlink_request(&nlh, AF_INET6, RTM_GETADDR) < 0) { status = -1; goto end_addr; } status = netlink_parse_info(netlink_if_address_filter, &nlh, NULL); end_addr: netlink_close(&nlh); return status; }
int kernel_setup_socket(int setup) { int rc; if(setup) { rc = netlink_socket(&nl_listen, RTMGRP_IPV6_ROUTE | RTMGRP_IPV4_ROUTE | RTMGRP_LINK | RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR); if(rc < 0) { perror("netlink_socket(RTMGRP_ROUTE | RTMGRP_LINK | RTMGRP_IFADDR)"); kernel_socket = -1; return -1; } kernel_socket = nl_listen.sock; return 1; } else { close(nl_listen.sock); nl_listen.sock = -1; kernel_socket = -1; return 1; } }
/* Adresses lookup bootstrap function */ static int netlink_address_lookup(void) { nl_handle_t nlh; int status = 0; if (netlink_socket(&nlh, 0, 0) < 0) return -1; /* IPv4 Address lookup */ if (netlink_request(&nlh, AF_INET, RTM_GETADDR) < 0) { status = -1; goto end_addr; } status = netlink_parse_info(netlink_if_address_filter, &nlh, NULL); /* IPv6 Address lookup */ if (netlink_request(&nlh, AF_INET6, RTM_GETADDR) < 0) { status = -1; goto end_addr; } status = netlink_parse_info(netlink_if_address_filter, &nlh, NULL); end_addr: netlink_close(&nlh); return status; }
/* Add remote address */ static int netlink_addroute(uint32_t ifa_index, uint8_t remote) { struct rtmsg *rtm; struct rtattr *rta; uint32_t reqlen = NLMSG_LENGTH(NLMSG_ALIGN(sizeof(*rtm)) + RTA_SPACE(1) + RTA_SPACE(sizeof(ifa_index))); struct req { struct nlmsghdr nlh; char buf[512]; } req = { .nlh = { .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE | NLM_F_APPEND, .nlmsg_type = RTM_NEWROUTE, .nlmsg_pid = getpid(), .nlmsg_len = reqlen, }, }; size_t buflen = sizeof(req.buf) - sizeof(*rtm); int fd; int error; struct sockaddr_nl addr = { .nl_family = AF_NETLINK, }; rtm = NLMSG_DATA(&req.nlh); rtm->rtm_family = AF_PHONET; rtm->rtm_dst_len = 6; rtm->rtm_src_len = 0; rtm->rtm_tos = 0; rtm->rtm_table = RT_TABLE_MAIN; rtm->rtm_protocol = RTPROT_STATIC; rtm->rtm_scope = RT_SCOPE_UNIVERSE; rtm->rtm_type = RTN_UNICAST; rtm->rtm_flags = 0; rta = IFA_RTA(rtm); rta->rta_type = RTA_DST; rta->rta_len = RTA_LENGTH(1); *(uint8_t *)RTA_DATA(rta) = remote; rta = RTA_NEXT(rta, buflen); rta->rta_type = RTA_OIF; rta->rta_len = RTA_LENGTH(sizeof(ifa_index)); *(uint32_t *)RTA_DATA(rta) = ifa_index; fd = netlink_socket(); if (fd == -1) return -errno; if (sendto(fd, &req, reqlen, 0, (void *)&addr, sizeof(addr)) == -1) error = -errno; else error = netlink_getack(fd); close(fd); return error; }
int netlink_get_interfaces(struct ifaces_list **iface_list) { int fd; int seq; int status; *iface_list = NULL; *iface_list = calloc(sizeof(struct ifaces_list), 1); if(*iface_list == NULL) { return ENOMEM; } fd = netlink_socket(NETLINK_ROUTE); if(fd == -1) { dprintf("failed with netlink"); return ERROR_NOT_SUPPORTED; } seq = netlink_request(fd, AF_UNSPEC, RTM_GETLINK); if(seq == -1) { dprintf("netlink_request RTM_GETLINK failed"); close(fd); return ERROR_NOT_SUPPORTED; } // will create one iface_entry for each interface status = netlink_parse(fd, seq, netlink_parse_interface_link, iface_list); if(status != 0) { if (*iface_list) free(*iface_list); *iface_list = NULL; return status; } seq = netlink_request(fd, AF_UNSPEC, RTM_GETADDR); if(seq == -1) { dprintf("netlink_request RTM_GETADDR failed"); close(fd); return ERROR_NOT_SUPPORTED; } // for each interface created before, will get the IPv4 / IPv6 addr status = netlink_parse(fd, seq, netlink_parse_interface_address, iface_list); close(fd); if(status != 0) { if (*iface_list) free(*iface_list); *iface_list = NULL; } return status; }
int socket(int domain, int type, int protocol) { libc_func(socket, int, int, int, int); int fd; fd = netlink_socket(domain, type, protocol); if (fd != UNHANDLED) return fd; return _socket(domain, type, protocol); }
int netlink_get_routing_table(struct ipv4_routing_table **table_ipv4, struct ipv6_routing_table **table_ipv6) { int fd; int seq; int status; struct routing_table table; *table_ipv4 = NULL; *table_ipv6 = NULL; table.table_ipv4 = table_ipv4; table.table_ipv6 = table_ipv6; *table_ipv4 = calloc(sizeof(struct ipv4_routing_table), 1); *table_ipv6 = calloc(sizeof(struct ipv6_routing_table), 1); if(*table_ipv4 == NULL) { return ENOMEM; } if(*table_ipv6 == NULL) { free(*table_ipv4); return ENOMEM; } fd = netlink_socket(NETLINK_ROUTE); if(fd == -1) { dprintf("failed with netlink"); return ERROR_NOT_SUPPORTED; } seq = netlink_request(fd, AF_UNSPEC, RTM_GETROUTE); if(seq == -1) { dprintf("netlink_request RTM_GETROUTE failed"); close(fd); return ERROR_NOT_SUPPORTED; } status = netlink_parse(fd, seq, netlink_parse_routing_table, &table); close(fd); if(status != 0) { if (*table_ipv4) free(*table_ipv4); if (*table_ipv6) free(*table_ipv6); *table_ipv4 = NULL; *table_ipv6 = NULL; } return status; }
GPhonetNetlink *g_pn_netlink_start(GIsiModem *idx, GPhonetNetlinkFunc callback, void *data) { GIOChannel *chan; GPhonetNetlink *self; int fd; unsigned group = RTNLGRP_LINK; unsigned interface = g_isi_modem_index(idx); fd = netlink_socket(); if (fd == -1) return NULL; self = calloc(1, sizeof(*self)); if (self == NULL) goto error; fcntl(fd, F_SETFL, O_NONBLOCK | fcntl(fd, F_GETFL)); if (setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group))) goto error; if (interface) bring_up(interface); g_pn_netlink_getlink(fd); chan = g_io_channel_unix_new(fd); if (chan == NULL) goto error; g_io_channel_set_close_on_unref(chan, TRUE); g_io_channel_set_encoding(chan, NULL, NULL); g_io_channel_set_buffered(chan, FALSE); self->callback = callback; self->opaque = data; self->interface = interface; self->watch = g_io_add_watch(chan, G_IO_IN|G_IO_ERR|G_IO_HUP, g_pn_nl_process, self); g_io_channel_unref(chan); netlink_list = g_slist_prepend(netlink_list, self); return self; error: close(fd); free(self); return NULL; }
int get_route_table_by_netlink(struct route_table *rt) { struct nlsock nl; int ret = netlink_socket(&nl, 0 ); get_route_table(nl); int i = 0; struct route* r = route_list; while(r) { i++; r = r->next; } rt->route_table_len = i; rt->routes = route_list; close(nl.sock); }
int getifaddrs(struct ifaddrs **ifap) { if(!ifap) { return -1; } *ifap = NULL; int l_socket = netlink_socket(); if(l_socket < 0) { return -1; } NetlinkList *l_linkResults = getResultList(l_socket, RTM_GETLINK); if(!l_linkResults) { close(l_socket); return -1; } NetlinkList *l_addrResults = getResultList(l_socket, RTM_GETADDR); if(!l_addrResults) { close(l_socket); freeResultList(l_linkResults); return -1; } unsigned l_numLinks = countLinks(l_socket, l_linkResults) + countLinks(l_socket, l_addrResults); struct ifaddrs *l_links[l_numLinks]; memset(l_links, 0, l_numLinks * sizeof(struct ifaddrs *)); if (interpret(l_socket, l_linkResults, l_links, ifap) == -1) { return -1; } if (interpret(l_socket, l_addrResults, l_links, ifap) == -1) { return -1; } freeResultList(l_linkResults); freeResultList(l_addrResults); close(l_socket); return 0; }
int getifaddrs(struct ifaddrs **ifap) { int l_socket; int l_result; int l_numLinks; NetlinkList *l_linkResults; NetlinkList *l_addrResults; if(!ifap) { return -1; } *ifap = NULL; l_socket = netlink_socket(); if(l_socket < 0) { return -1; } l_linkResults = getResultList(l_socket, RTM_GETLINK); if(!l_linkResults) { close(l_socket); return -1; } l_addrResults = getResultList(l_socket, RTM_GETADDR); if(!l_addrResults) { close(l_socket); freeResultList(l_linkResults); return -1; } l_result = 0; l_numLinks = interpretLinks(l_socket, l_linkResults, ifap); if(l_numLinks == -1 || interpretAddrs(l_socket, l_addrResults, ifap, l_numLinks) == -1) { l_result = -1; } freeResultList(l_linkResults); freeResultList(l_addrResults); close(l_socket); return l_result; }
/* Interfaces lookup bootstrap function */ int netlink_interface_lookup(void) { nl_handle_t nlh; int status = 0; if (netlink_socket(&nlh, 0, 0) < 0) return -1; /* Interface lookup */ if (netlink_request(&nlh, AF_PACKET, RTM_GETLINK) < 0) { status = -1; goto end_int; } status = netlink_parse_info(netlink_if_link_filter, &nlh, NULL); end_int: netlink_close(&nlh); return status; }
/* This function should not return routes installed by us. */ int kernel_routes(struct kernel_route *routes, int maxroutes) { int i, rc; int maxr = maxroutes; int found = 0; void *data[3] = { &maxr, routes, &found }; int families[2] = { AF_INET6, AF_INET }; struct rtgenmsg g; if(!nl_setup) { fprintf(stderr,"kernel_routes: netlink not initialized.\n"); errno = EIO; return -1; } if(nl_command.sock < 0) { rc = netlink_socket(&nl_command, 0); if(rc < 0) { perror("kernel_routes: netlink_socket()"); return -1; } } for(i = 0; i < 2; i++) { memset(&g, 0, sizeof(g)); g.rtgen_family = families[i]; rc = netlink_send_dump(RTM_GETROUTE, &g, sizeof(g)); if(rc < 0) return -1; rc = netlink_read(&nl_command, NULL, 1, filter_kernel_routes, (void *)data); if(rc < 0) return -1; } return found; }
int kernel_addresses(char *ifname, int ifindex, int ll, struct kernel_route *routes, int maxroutes) { int maxr = maxroutes; int found = 0; void *data[] = { &maxr, routes, &found, &ifindex, &ll, NULL }; struct rtgenmsg g; int rc; if (!nl_setup) { fprintf(stderr, "kernel_addresses: netlink not initialized.\n"); errno = ENOSYS; return -1; } if (nl_command.sock < 0) { rc = netlink_socket(&nl_command, 0); if (rc < 0) { int save = errno; perror("kernel_addresses: netlink_socket()"); errno = save; return -1; } } memset(&g, 0, sizeof(g)); g.rtgen_family = AF_UNSPEC; rc = netlink_send_dump(RTM_GETADDR, &g, sizeof(g)); if (rc < 0) return -1; rc = netlink_read(&nl_command, NULL, 1, filter_addresses, (void*)data); if (rc < 0) return -1; return found; }
int netlink_get_ipv4_routing_table(struct ipv4_routing_table **table) { *table = NULL; int fd; int seq; int status; *table = calloc(sizeof(struct ipv4_routing_table), 1); if(*table == NULL) { return ENOMEM; } fd = netlink_socket(NETLINK_ROUTE); if(fd == -1) { dprintf("[%s] failed with netlink", __FUNCTION__); return ERROR_NOT_SUPPORTED; } seq = netlink_request(fd, AF_INET, RTM_GETROUTE); if(seq == -1) { dprintf("[%s] netlink_request failed", __FUNCTION__); close(fd); return ERROR_NOT_SUPPORTED; } status = netlink_parse(fd, seq, netlink_parse_ipv4_routing_table, table); close(fd); if(status != 0 && *table) { free(*table); *table = NULL; } return status; }
int kernel_setup(int setup) { int rc; if(setup) { if(export_table < 0) export_table = RT_TABLE_MAIN; if(import_table < 0) import_table = RT_TABLE_MAIN; dgram_socket = socket(PF_INET, SOCK_DGRAM, 0); if(dgram_socket < 0) return -1; rc = netlink_socket(&nl_command, 0); if(rc < 0) { perror("netlink_socket(0)"); return -1; } nl_setup = 1; old_forwarding = read_proc("/proc/sys/net/ipv6/conf/all/forwarding"); if(old_forwarding < 0) { perror("Couldn't read forwarding knob."); return -1; } rc = write_proc("/proc/sys/net/ipv6/conf/all/forwarding", 1); if(rc < 0) { perror("Couldn't write forwarding knob."); return -1; } old_ipv4_forwarding = read_proc("/proc/sys/net/ipv4/conf/all/forwarding"); if(old_ipv4_forwarding < 0) { perror("Couldn't read IPv4 forwarding knob."); return -1; } rc = write_proc("/proc/sys/net/ipv4/conf/all/forwarding", 1); if(rc < 0) { perror("Couldn't write IPv4 forwarding knob."); return -1; } old_accept_redirects = read_proc("/proc/sys/net/ipv6/conf/all/accept_redirects"); if(old_accept_redirects < 0) { perror("Couldn't read accept_redirects knob."); return -1; } rc = write_proc("/proc/sys/net/ipv6/conf/all/accept_redirects", 0); if(rc < 0) { perror("Couldn't write accept_redirects knob."); return -1; } old_rp_filter = read_proc("/proc/sys/net/ipv4/conf/all/rp_filter"); if(old_rp_filter < 0) { perror("Couldn't read rp_filter knob."); return -1; } rc = write_proc("/proc/sys/net/ipv4/conf/all/rp_filter", 0); if(rc < 0) { perror("Couldn't write rp_filter knob."); return -1; } return 1; } else { close(dgram_socket); dgram_socket = -1; if(old_forwarding >= 0) { rc = write_proc("/proc/sys/net/ipv6/conf/all/forwarding", old_forwarding); if(rc < 0) { perror("Couldn't write forwarding knob.\n"); return -1; } } if(old_ipv4_forwarding >= 0) { rc = write_proc("/proc/sys/net/ipv4/conf/all/forwarding", old_ipv4_forwarding); if(rc < 0) { perror("Couldn't write IPv4 forwarding knob.\n"); return -1; } } if(old_accept_redirects >= 0) { rc = write_proc("/proc/sys/net/ipv6/conf/all/accept_redirects", old_accept_redirects); if(rc < 0) { perror("Couldn't write accept_redirects knob.\n"); return -1; } } if(old_rp_filter >= 0) { rc = write_proc("/proc/sys/net/ipv4/conf/all/rp_filter", old_rp_filter); if(rc < 0) { perror("Couldn't write rp_filter knob.\n"); return -1; } } close(nl_command.sock); nl_command.sock = -1; nl_setup = 0; return 1; } }
int kernel_route(int operation, const unsigned char *dest, unsigned short plen, const unsigned char *gate, int ifindex, unsigned int metric, const unsigned char *newgate, int newifindex, unsigned int newmetric) { union { char raw[1024]; struct nlmsghdr nh; } buf; struct rtmsg *rtm; struct rtattr *rta; int len = sizeof(buf.raw); int rc, ipv4; if(!nl_setup) { fprintf(stderr,"kernel_route: netlink not initialized.\n"); errno = EIO; return -1; } /* if the socket has been closed after an IO error, */ /* we try to re-open it. */ if(nl_command.sock < 0) { rc = netlink_socket(&nl_command, 0); if(rc < 0) { int olderrno = errno; perror("kernel_route: netlink_socket()"); errno = olderrno; return -1; } } /* Check that the protocol family is consistent. */ if(plen >= 96 && v4mapped(dest)) { if(!v4mapped(gate)) { errno = EINVAL; return -1; } } else { if(v4mapped(gate)) { errno = EINVAL; return -1; } } ipv4 = v4mapped(gate); if(operation == ROUTE_MODIFY) { if(newmetric == metric && memcmp(newgate, gate, 16) == 0 && newifindex == ifindex) return 0; /* It would be better to add the new route before removing the old one, to avoid losing packets. However, this causes problems with non-multipath kernels, which sometimes silently fail the request, causing "stuck" routes. Let's stick with the naive approach, and hope that the window is small enough to be negligible. */ kernel_route(ROUTE_FLUSH, dest, plen, gate, ifindex, metric, NULL, 0, 0); rc = kernel_route(ROUTE_ADD, dest, plen, newgate, newifindex, newmetric, NULL, 0, 0); if(rc < 0) { if(errno == EEXIST) rc = 1; /* Should we try to re-install the flushed route on failure? Error handling is hard. */ } return rc; } kdebugf("kernel_route: %s %s/%d metric %d dev %d nexthop %s\n", operation == ROUTE_ADD ? "add" : operation == ROUTE_FLUSH ? "flush" : "???", format_address(dest), plen, metric, ifindex, format_address(gate)); /* Unreachable default routes cause all sort of weird interactions; ignore them. */ if(metric >= KERNEL_INFINITY && (plen == 0 || (ipv4 && plen == 96))) return 0; memset(buf.raw, 0, sizeof(buf.raw)); if(operation == ROUTE_ADD) { buf.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; buf.nh.nlmsg_type = RTM_NEWROUTE; } else { buf.nh.nlmsg_flags = NLM_F_REQUEST; buf.nh.nlmsg_type = RTM_DELROUTE; } rtm = NLMSG_DATA(&buf.nh); rtm->rtm_family = ipv4 ? AF_INET : AF_INET6; rtm->rtm_dst_len = ipv4 ? plen - 96 : plen; rtm->rtm_table = export_table; rtm->rtm_scope = RT_SCOPE_UNIVERSE; if(metric < KERNEL_INFINITY) rtm->rtm_type = RTN_UNICAST; else rtm->rtm_type = RTN_UNREACHABLE; rtm->rtm_protocol = RTPROT_BABEL; rtm->rtm_flags |= RTNH_F_ONLINK; rta = RTM_RTA(rtm); if(ipv4) { rta = RTA_NEXT(rta, len); rta->rta_len = RTA_LENGTH(sizeof(struct in_addr)); rta->rta_type = RTA_DST; memcpy(RTA_DATA(rta), dest + 12, sizeof(struct in_addr)); } else { rta = RTA_NEXT(rta, len); rta->rta_len = RTA_LENGTH(sizeof(struct in6_addr)); rta->rta_type = RTA_DST; memcpy(RTA_DATA(rta), dest, sizeof(struct in6_addr)); } rta = RTA_NEXT(rta, len); rta->rta_len = RTA_LENGTH(sizeof(int)); rta->rta_type = RTA_PRIORITY; if(metric < KERNEL_INFINITY) { *(int*)RTA_DATA(rta) = metric; rta = RTA_NEXT(rta, len); rta->rta_len = RTA_LENGTH(sizeof(int)); rta->rta_type = RTA_OIF; *(int*)RTA_DATA(rta) = ifindex; if(ipv4) { rta = RTA_NEXT(rta, len); rta->rta_len = RTA_LENGTH(sizeof(struct in_addr)); rta->rta_type = RTA_GATEWAY; memcpy(RTA_DATA(rta), gate + 12, sizeof(struct in_addr)); } else { rta = RTA_NEXT(rta, len); rta->rta_len = RTA_LENGTH(sizeof(struct in6_addr)); rta->rta_type = RTA_GATEWAY; memcpy(RTA_DATA(rta), gate, sizeof(struct in6_addr)); } } else { *(int*)RTA_DATA(rta) = -1; } buf.nh.nlmsg_len = (char*)rta + rta->rta_len - buf.raw; return netlink_talk(&buf.nh); }
static struct Interface * main_loop(int sock, struct Interface *ifaces, char const *conf_path) { struct pollfd fds[2]; sigset_t sigmask; sigset_t sigempty; struct sigaction sa; sigemptyset(&sigempty); sigemptyset(&sigmask); sigaddset(&sigmask, SIGHUP); sigaddset(&sigmask, SIGTERM); sigaddset(&sigmask, SIGINT); sigaddset(&sigmask, SIGUSR1); sigprocmask(SIG_BLOCK, &sigmask, NULL); sa.sa_handler = sighup_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGHUP, &sa, 0); sa.sa_handler = sigterm_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGTERM, &sa, 0); sa.sa_handler = sigint_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGINT, &sa, 0); sa.sa_handler = sigusr1_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGUSR1, &sa, 0); memset(fds, 0, sizeof(fds)); fds[0].fd = sock; fds[0].events = POLLIN; #if HAVE_NETLINK fds[1].fd = netlink_socket(); fds[1].events = POLLIN; #else fds[1].fd = -1; #endif for (;;) { struct timespec *tsp = 0; struct Interface *next_iface_to_expire = find_iface_by_time(ifaces); if (next_iface_to_expire) { static struct timespec ts; int timeout = next_time_msec(next_iface_to_expire); ts.tv_sec = timeout / 1000; ts.tv_nsec = (timeout - 1000 * ts.tv_sec) * 1000000; tsp = &ts; dlog(LOG_DEBUG, 1, "polling for %g second(s), next iface is %s", timeout / 1000.0, next_iface_to_expire->props.name); } else { dlog(LOG_DEBUG, 1, "no iface is next. Polling indefinitely"); } #ifdef HAVE_PPOLL int rc = ppoll(fds, sizeof(fds) / sizeof(fds[0]), tsp, &sigempty); #else int rc = poll(fds, sizeof(fds) / sizeof(fds[0]), 1000*tsp->tv_sec); #endif if (rc > 0) { #ifdef HAVE_NETLINK if (fds[1].revents & (POLLERR | POLLHUP | POLLNVAL)) { flog(LOG_WARNING, "socket error on fds[1].fd"); } else if (fds[1].revents & POLLIN) { process_netlink_msg(fds[1].fd, ifaces); } #endif if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { flog(LOG_WARNING, "socket error on fds[0].fd"); } else if (fds[0].revents & POLLIN) { int len, hoplimit; struct sockaddr_in6 rcv_addr; struct in6_pktinfo *pkt_info = NULL; unsigned char msg[MSG_SIZE_RECV]; unsigned char chdr[CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int))]; len = recv_rs_ra(sock, msg, &rcv_addr, &pkt_info, &hoplimit, chdr); if (len > 0 && pkt_info) { process(sock, ifaces, msg, len, &rcv_addr, pkt_info, hoplimit); } else if (!pkt_info) { dlog(LOG_INFO, 4, "recv_rs_ra returned null pkt_info"); } else if (len <= 0) { dlog(LOG_INFO, 4, "recv_rs_ra returned len <= 0: %d", len); } } } else if (rc == 0) { if (next_iface_to_expire) timer_handler(sock, next_iface_to_expire); } else if (rc == -1) { dlog(LOG_INFO, 3, "poll returned early: %s", strerror(errno)); } if (sigint_received) { flog(LOG_WARNING, "exiting, %d sigint(s) received", sigint_received); break; } if (sigterm_received) { flog(LOG_WARNING, "exiting, %d sigterm(s) received", sigterm_received); break; } if (sighup_received) { dlog(LOG_INFO, 3, "sig hup received"); ifaces = reload_config(sock, ifaces, conf_path); sighup_received = 0; } if (sigusr1_received) { dlog(LOG_INFO, 3, "sig usr1 received"); reset_prefix_lifetimes(ifaces); sigusr1_received = 0; } } return ifaces; }
void main_loop(void) { struct pollfd fds[2]; memset(fds, 0, sizeof(fds)); fds[0].fd = sock; fds[0].events = POLLIN; fds[0].revents = 0; #if HAVE_NETLINK fds[1].fd = netlink_socket(); fds[1].events = POLLIN; fds[1].revents = 0; #else fds[1].fd = -1; fds[1].events = 0; fds[1].revents = 0; #endif for (;;) { struct Interface *next = NULL; struct Interface *iface; int timeout = -1; int rc; if (IfaceList) { timeout = next_time_msec(IfaceList); next = IfaceList; for (iface = IfaceList; iface; iface = iface->next) { int t; t = next_time_msec(iface); if (timeout > t) { timeout = t; next = iface; } } } dlog(LOG_DEBUG, 5, "polling for %g seconds.", timeout/1000.0); rc = poll(fds, sizeof(fds)/sizeof(fds[0]), timeout); if (rc > 0) { if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { flog(LOG_WARNING, "socket error on fds[0].fd"); } else if (fds[0].revents & POLLIN) { int len, hoplimit; struct sockaddr_in6 rcv_addr; struct in6_pktinfo *pkt_info = NULL; unsigned char msg[MSG_SIZE_RECV]; len = recv_rs_ra(msg, &rcv_addr, &pkt_info, &hoplimit); if (len > 0) { process(IfaceList, msg, len, &rcv_addr, pkt_info, hoplimit); } } #ifdef HAVE_NETLINK if (fds[1].revents & (POLLERR | POLLHUP | POLLNVAL)) { flog(LOG_WARNING, "socket error on fds[1].fd"); } else if (fds[1].revents & POLLIN) { process_netlink_msg(fds[1].fd); } #endif } else if ( rc == 0 ) { if (next) timer_handler(next); } else if ( rc == -1 && errno != EINTR ) { flog(LOG_ERR, "poll error: %s", strerror(errno)); } if (sigterm_received || sigint_received) { flog(LOG_WARNING, "Exiting, sigterm or sigint received.\n"); break; } if (sighup_received) { reload_config(); sighup_received = 0; } if (sigusr1_received) { reset_prefix_lifetimes(); sigusr1_received = 0; } } }
static int netlink_getack(int fd) { struct { struct nlmsghdr nlh; char buf[SIZE_NLMSG]; } resp; struct iovec iov = { &resp, sizeof(resp), }; struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1, }; ssize_t ret; struct nlmsghdr *nlh = &resp.nlh; ret = recvmsg(fd, &msg, 0); if (ret == -1) return -errno; if (msg.msg_flags & MSG_TRUNC) return -EIO; for (; NLMSG_OK(nlh, (size_t)ret); nlh = NLMSG_NEXT(nlh, ret)) { if (nlh->nlmsg_type == NLMSG_DONE) return 0; if (nlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = NLMSG_DATA(nlh); return err->error; } } return -EIO; } /* Set local address */ static int netlink_setaddr(uint32_t ifa_index, uint8_t ifa_local) { struct ifaddrmsg *ifa; struct rtattr *rta; uint32_t reqlen = NLMSG_LENGTH(NLMSG_ALIGN(sizeof(*ifa)) + RTA_SPACE(1)); struct req { struct nlmsghdr nlh; char buf[512]; } req = { .nlh = { .nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK, .nlmsg_type = RTM_NEWADDR, .nlmsg_pid = getpid(), .nlmsg_len = reqlen, }, }; int fd; int error; struct sockaddr_nl addr = { .nl_family = AF_NETLINK, }; ifa = NLMSG_DATA(&req.nlh); ifa->ifa_family = AF_PHONET; ifa->ifa_prefixlen = 0; ifa->ifa_index = ifa_index; rta = IFA_RTA(ifa); rta->rta_type = IFA_LOCAL; rta->rta_len = RTA_LENGTH(1); *(uint8_t *)RTA_DATA(rta) = ifa_local; fd = netlink_socket(); if (fd == -1) return -errno; if (sendto(fd, &req, reqlen, 0, (void *)&addr, sizeof(addr)) == -1) error = -errno; else error = netlink_getack(fd); close(fd); return error; } int g_pn_netlink_set_address(GIsiModem *idx, uint8_t local) { uint32_t ifindex = g_isi_modem_index(idx); if (ifindex == 0) return -ENODEV; if (local != PN_DEV_PC && local != PN_DEV_SOS) return -EINVAL; return netlink_setaddr(ifindex, local); }