diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9474582 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +Makefile +CMakeCache.txt +CMakeFiles +*.cmake +install_manifest.txt +relayd diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7e960ab --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 2.8) + +PROJECT(relayd C) +ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3) + +IF(APPLE) + INCLUDE_DIRECTORIES(/opt/local/include) + LINK_DIRECTORIES(/opt/local/lib) +ENDIF() + +ADD_EXECUTABLE(relayd main.c dhcp.c route.c) +TARGET_LINK_LIBRARIES(relayd ubox) + +SET(CMAKE_INSTALL_PREFIX /usr) + +INSTALL(TARGETS relayd + RUNTIME DESTINATION sbin +) diff --git a/dhcp.c b/dhcp.c new file mode 100644 index 0000000..aefe34f --- /dev/null +++ b/dhcp.c @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2010 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include "relayd.h" + +struct ip_packet { + struct ether_header eth; + struct iphdr iph; +} __packed; + + +enum { + DHCP_OPTION_ROUTER = 0x03, + DHCP_OPTION_ROUTES = 0x79, + DHCP_OPTION_END = 0xff, +}; + +struct dhcp_option { + uint8_t code; + uint8_t len; + uint8_t data[]; +}; + +struct dhcp_header { + uint8_t op, htype, hlen, hops; + uint32_t xit; + uint16_t secs, flags; + struct in_addr ciaddr, yiaddr, siaddr, giaddr; + unsigned char chaddr[16]; + unsigned char sname[64]; + unsigned char file[128]; + uint32_t cookie; + uint8_t option_data[]; +} __packed; + +static uint16_t +chksum(uint16_t sum, const uint8_t *data, uint16_t len) +{ + const uint8_t *last; + uint16_t t; + + last = data + len - 1; + + while(data < last) { + t = (data[0] << 8) + data[1]; + sum += t; + if(sum < t) + sum++; + data += 2; + } + + if(data == last) { + t = (data[0] << 8) + 0; + sum += t; + if(sum < t) + sum++; + } + + return sum; +} + +static void +parse_dhcp_options(struct relayd_host *host, struct dhcp_header *dhcp, int len) +{ + uint8_t *end = (uint8_t *) dhcp + len; + struct dhcp_option *opt = (void *)dhcp->option_data; + static const uint8_t dest[4] = { 0, 0, 0, 0 }; + + while((uint8_t *) opt < end) { + if ((uint8_t *) opt + opt->len > end) + break; + + opt = (void *) &opt->data[opt->len]; + switch(opt->code) { + case DHCP_OPTION_ROUTER: + DPRINTF(2, "Found a DHCP router option, len=%d\n", opt->len); + if (!memcmp(opt->data, host->ipaddr, 4)) + relayd_add_host_route(host, dest, 0); + else + relayd_add_pending_route(opt->data, dest, 0, 10000); + break; + case DHCP_OPTION_ROUTES: + DPRINTF(2, "Found a DHCP static routes option, len=%d\n", opt->len); + break; + case DHCP_OPTION_END: + opt = (void *) end; + continue; + default: + DPRINTF(3, "Skipping unknown DHCP option %02x\n", opt->code); + continue; + } + + } +} + +bool relayd_handle_dhcp_packet(struct relayd_interface *rif, void *data, int len, bool forward, bool parse) +{ + struct ip_packet *pkt = data; + struct udphdr *udp; + struct dhcp_header *dhcp; + struct relayd_host *host; + int udplen; + uint16_t sum; + + if (pkt->eth.ether_type != htons(ETH_P_IP)) + return false; + + if (pkt->iph.version != 4) + return false; + + if (pkt->iph.protocol != IPPROTO_UDP) + return false; + + udp = (void *) ((char *) &pkt->iph + (pkt->iph.ihl << 2)); + dhcp = (void *) (udp + 1); + + udplen = ntohs(udp->len); + if (udplen > len - ((char *) udp - (char *) data)) + return false; + + if (udp->dest != htons(67) && udp->source != htons(67)) + return false; + + if (dhcp->op != 1 && dhcp->op != 2) + return false; + + if (!forward) + return true; + + if (dhcp->op == 2) { + host = relayd_refresh_host(rif, pkt->eth.ether_shost, (void *) &pkt->iph.saddr); + if (host && parse) + parse_dhcp_options(host, dhcp, udplen - sizeof(struct udphdr)); + } + + DPRINTF(2, "%s: handling DHCP %s\n", rif->ifname, (dhcp->op == 1 ? "request" : "response")); + + dhcp->flags |= htons(DHCP_FLAG_BROADCAST); + + udp->check = 0; + sum = udplen + IPPROTO_UDP; + sum = chksum(sum, (void *) &pkt->iph.saddr, 8); + sum = chksum(sum, (void *) udp, udplen); + if (sum == 0) + sum = 0xffff; + + udp->check = htons(~sum); + + relayd_forward_bcast_packet(rif, data, len); + + return true; +} + + diff --git a/main.c b/main.c new file mode 100644 index 0000000..6727aa2 --- /dev/null +++ b/main.c @@ -0,0 +1,862 @@ +/* + * Copyright (C) 2010 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "relayd.h" + +static LIST_HEAD(pending_routes); +LIST_HEAD(interfaces); +int debug; + +static int host_timeout; +static int host_ping_tries; +static int inet_sock; +static int forward_bcast; +static int forward_dhcp; +static int parse_dhcp; + +uint8_t local_addr[4]; +int local_route_table; + +struct relayd_pending_route { + struct relayd_route rt; + struct uloop_timeout timeout; + uint8_t gateway[4]; +}; + +static struct relayd_host *find_host_by_ipaddr(struct relayd_interface *rif, const uint8_t *ipaddr) +{ + struct relayd_host *host; + + if (!rif) { + list_for_each_entry(rif, &interfaces, list) { + host = find_host_by_ipaddr(rif, ipaddr); + if (!host) + continue; + + return host; + } + return NULL; + } + + list_for_each_entry(host, &rif->hosts, list) { + if (memcmp(ipaddr, host->ipaddr, sizeof(host->ipaddr)) != 0) + continue; + + return host; + } + return NULL; +} + +static void add_arp(struct relayd_host *host) +{ + struct sockaddr_in *sin; + struct arpreq arp; + + strncpy(arp.arp_dev, host->rif->ifname, sizeof(arp.arp_dev)); + arp.arp_flags = ATF_COM; + + arp.arp_ha.sa_family = ARPHRD_ETHER; + memcpy(arp.arp_ha.sa_data, host->lladdr, ETH_ALEN); + + sin = (struct sockaddr_in *) &arp.arp_pa; + sin->sin_family = AF_INET; + memcpy(&sin->sin_addr, host->ipaddr, sizeof(host->ipaddr)); + + ioctl(inet_sock, SIOCSARP, &arp); +} + +static void timeout_host_route(struct uloop_timeout *timeout) +{ + struct relayd_pending_route *rt; + + rt = container_of(timeout, struct relayd_pending_route, timeout); + list_del(&rt->rt.list); + free(rt); +} + +void relayd_add_host_route(struct relayd_host *host, const uint8_t *dest, uint8_t mask) +{ + struct relayd_route *rt; + + list_for_each_entry(rt, &host->routes, list) { + if (!memcmp(rt->dest, dest, sizeof(rt->dest)) && rt->mask == mask) + return; + } + + rt = calloc(1, sizeof(*rt)); + if (!rt) + return; + + list_add(&rt->list, &host->routes); + memcpy(rt->dest, dest, sizeof(rt->dest)); + rt->mask = mask; + relayd_add_route(host, rt); +} + +static void del_host(struct relayd_host *host) +{ + struct relayd_route *route, *tmp; + + DPRINTF(1, "%s: deleting host "IP_FMT" ("MAC_FMT")\n", host->rif->ifname, + IP_BUF(host->ipaddr), MAC_BUF(host->lladdr)); + + list_for_each_entry_safe(route, tmp, &host->routes, list) { + relayd_del_route(host, route); + list_del(&route->list); + free(route); + } + if (host->rif->managed) + relayd_del_route(host, NULL); + uloop_timeout_cancel(&host->timeout); + list_del(&host->list); + free(host); +} + +static void fill_arp_packet(struct arp_packet *pkt, struct relayd_interface *rif, + const uint8_t spa[4], const uint8_t tpa[4]) +{ + memset(pkt, 0, sizeof(*pkt)); + + pkt->eth.ether_type = htons(ETHERTYPE_ARP); + memcpy(pkt->eth.ether_shost, rif->sll.sll_addr, ETH_ALEN); + + memcpy(pkt->arp.arp_sha, rif->sll.sll_addr, ETH_ALEN); + memcpy(pkt->arp.arp_spa, spa, 4); + memcpy(pkt->arp.arp_tpa, tpa, 4); + + pkt->arp.arp_hrd = htons(ARPHRD_ETHER); + pkt->arp.arp_pro = htons(ETH_P_IP); + pkt->arp.arp_hln = ETH_ALEN; + pkt->arp.arp_pln = 4; +} + +static void send_arp_request(struct relayd_interface *rif, const uint8_t *ipaddr) +{ + struct arp_packet pkt; + + fill_arp_packet(&pkt, rif, rif->src_ip, ipaddr); + + pkt.arp.arp_op = htons(ARPOP_REQUEST); + memcpy(pkt.arp.arp_spa, rif->src_ip, ETH_ALEN); + memset(pkt.arp.arp_tha, 0, ETH_ALEN); + memset(pkt.eth.ether_dhost, 0xff, ETH_ALEN); + + DPRINTF(2, "%s: sending ARP who-has "IP_FMT", tell "IP_FMT" ("MAC_FMT")\n", + rif->ifname, IP_BUF(pkt.arp.arp_tpa), + IP_BUF(pkt.arp.arp_spa), MAC_BUF(pkt.eth.ether_shost)); + + sendto(rif->fd.fd, &pkt, sizeof(pkt), 0, + (struct sockaddr *) &rif->sll, sizeof(rif->sll)); +} + +void relayd_add_pending_route(const uint8_t *gateway, const uint8_t *dest, uint8_t mask, int timeout) +{ + struct relayd_pending_route *rt; + struct relayd_interface *rif; + struct relayd_host *host; + + host = find_host_by_ipaddr(NULL, gateway); + if (host) { + relayd_add_host_route(host, dest, mask); + return; + } + + rt = calloc(1, sizeof(*rt)); + if (!rt) + return; + + memcpy(rt->gateway, gateway, sizeof(rt->gateway)); + memcpy(rt->rt.dest, dest, sizeof(rt->rt.dest)); + rt->rt.mask = mask; + list_add(&rt->rt.list, &pending_routes); + if (timeout <= 0) + return; + + rt->timeout.cb = timeout_host_route; + uloop_timeout_set(&rt->timeout, 10000); + list_for_each_entry(rif, &interfaces, list) { + send_arp_request(rif, gateway); + } +} + +static void send_arp_reply(struct relayd_interface *rif, const uint8_t spa[4], + const uint8_t tha[ETH_ALEN], const uint8_t tpa[4]) +{ + struct arp_packet pkt; + + fill_arp_packet(&pkt, rif, spa, tpa); + + if (tha) { + pkt.arp.arp_op = htons(ARPOP_REPLY); + memcpy(pkt.eth.ether_dhost, tha, ETH_ALEN); + memcpy(pkt.arp.arp_tha, tha, ETH_ALEN); + + DPRINTF(2, "%s: sending ARP reply to "IP_FMT", "IP_FMT" is at ("MAC_FMT")\n", + rif->ifname, IP_BUF(pkt.arp.arp_tpa), + IP_BUF(pkt.arp.arp_spa), MAC_BUF(pkt.eth.ether_shost)); + } else { + pkt.arp.arp_op = htons(ARPOP_REQUEST); + memset(pkt.eth.ether_dhost, 0xff, ETH_ALEN); + memset(pkt.arp.arp_tha, 0xff, ETH_ALEN); + + DPRINTF(2, "%s: sending gratuitous ARP: "IP_FMT" is at ("MAC_FMT")\n", + rif->ifname, IP_BUF(pkt.arp.arp_tpa), + MAC_BUF(pkt.eth.ether_shost)); + } + + sendto(rif->fd.fd, &pkt, sizeof(pkt), 0, + (struct sockaddr *) &rif->sll, sizeof(rif->sll)); + + if (tha) + return; + + /* + * Gratuitous ARP comes in two flavours, request and reply. + * Some operating systems only accept request, some only reply. + * Let's just send both... + */ + pkt.arp.arp_op = htons(ARPOP_REPLY); + + sendto(rif->fd.fd, &pkt, sizeof(pkt), 0, + (struct sockaddr *) &rif->sll, sizeof(rif->sll)); + +} + +static void host_entry_timeout(struct uloop_timeout *timeout) +{ + struct relayd_host *host = container_of(timeout, struct relayd_host, timeout); + struct relayd_interface *rif; + + /* + * When a host is behind a managed interface, we must not expire its host + * entry prematurely, as this will cause routes to the node to expire, + * leading to loss of connectivity from the other side. + * When the timeout is reached, try pinging the host a few times before + * giving up on it. + */ + if (host->rif->managed && host->cleanup_pending < host_ping_tries) { + list_for_each_entry(rif, &interfaces, list) { + send_arp_request(rif, host->ipaddr); + } + host->cleanup_pending++; + uloop_timeout_set(&host->timeout, 1000); + return; + } + del_host(host); +} + +static struct relayd_host *add_host(struct relayd_interface *rif, const uint8_t *lladdr, const uint8_t *ipaddr) +{ + struct relayd_host *host; + struct relayd_pending_route *route, *rtmp; + + DPRINTF(1, "%s: adding host "IP_FMT" ("MAC_FMT")\n", rif->ifname, + IP_BUF(ipaddr), MAC_BUF(lladdr)); + + host = calloc(1, sizeof(*host)); + INIT_LIST_HEAD(&host->routes); + host->rif = rif; + memcpy(host->ipaddr, ipaddr, sizeof(host->ipaddr)); + memcpy(host->lladdr, lladdr, sizeof(host->lladdr)); + list_add(&host->list, &rif->hosts); + host->timeout.cb = host_entry_timeout; + uloop_timeout_set(&host->timeout, host_timeout * 1000); + + add_arp(host); + if (rif->managed) + relayd_add_route(host, NULL); + + list_for_each_entry_safe(route, rtmp, &pending_routes, rt.list) { + if (memcmp(route->gateway, ipaddr, 4) != 0) + continue; + + relayd_add_host_route(host, route->rt.dest, route->rt.mask); + if (!route->timeout.pending) + continue; + + uloop_timeout_cancel(&route->timeout); + list_del(&route->rt.list); + free(route); + } + + return host; +} + +static void send_gratuitous_arp(struct relayd_interface *rif, const uint8_t *spa) +{ + struct relayd_interface *to_rif; + + list_for_each_entry(to_rif, &interfaces, list) { + if (rif == to_rif) + continue; + + send_arp_reply(to_rif, spa, NULL, spa); + } +} + + +struct relayd_host *relayd_refresh_host(struct relayd_interface *rif, const uint8_t *lladdr, const uint8_t *ipaddr) +{ + struct relayd_host *host; + + host = find_host_by_ipaddr(rif, ipaddr); + if (!host) { + host = find_host_by_ipaddr(NULL, ipaddr); + + /* + * When we suddenly see the host appearing on a different interface, + * reduce the timeout to make the old entry expire faster, in case the + * host has moved. + * If the old entry is behind a managed interface, it will be pinged + * before we expire it + */ + if (host && !host->cleanup_pending) { + uloop_timeout_set(&host->timeout, 1); + return NULL; + } + + host = add_host(rif, lladdr, ipaddr); + } else { + host->cleanup_pending = false; + uloop_timeout_set(&host->timeout, host_timeout * 1000); + send_gratuitous_arp(rif, ipaddr); + } + + return host; +} + +static void relay_arp_request(struct relayd_interface *from_rif, struct arp_packet *pkt) +{ + struct relayd_interface *rif; + struct arp_packet reqpkt; + + memcpy(&reqpkt, pkt, sizeof(reqpkt)); + list_for_each_entry(rif, &interfaces, list) { + if (rif == from_rif) + continue; + + memcpy(reqpkt.eth.ether_shost, rif->sll.sll_addr, ETH_ALEN); + memset(reqpkt.eth.ether_dhost, 0xff, ETH_ALEN); + memcpy(reqpkt.arp.arp_sha, rif->sll.sll_addr, ETH_ALEN); + memset(reqpkt.arp.arp_tha, 0, ETH_ALEN); + + DPRINTF(2, "%s: sending ARP who-has "IP_FMT", tell "IP_FMT" ("MAC_FMT")\n", + rif->ifname, IP_BUF(reqpkt.arp.arp_tpa), + IP_BUF(reqpkt.arp.arp_spa), MAC_BUF(reqpkt.eth.ether_shost)); + + sendto(rif->fd.fd, &reqpkt, sizeof(reqpkt), 0, + (struct sockaddr *) &rif->sll, sizeof(rif->sll)); + } +} + +static void recv_arp_request(struct relayd_interface *rif, struct arp_packet *pkt) +{ + struct relayd_host *host; + + DPRINTF(2, "%s: ARP who-has "IP_FMT", tell "IP_FMT" ("MAC_FMT")\n", + rif->ifname, + IP_BUF(pkt->arp.arp_tpa), + IP_BUF(pkt->arp.arp_spa), + MAC_BUF(pkt->eth.ether_shost)); + + if (!memcmp(pkt->arp.arp_spa, "\x00\x00\x00\x00", 4)) + return; + + host = find_host_by_ipaddr(NULL, pkt->arp.arp_spa); + if (!host || host->rif != rif) + relayd_refresh_host(rif, pkt->eth.ether_shost, pkt->arp.arp_spa); + + if (local_route_table && !memcmp(pkt->arp.arp_tpa, local_addr, sizeof(local_addr))) { + send_arp_reply(rif, local_addr, pkt->arp.arp_sha, pkt->arp.arp_spa); + return; + } + + host = find_host_by_ipaddr(NULL, pkt->arp.arp_tpa); + + /* + * If a host is being pinged because of a timeout, do not use the cached + * entry here. That way we can avoid giving out stale data in case the node + * has moved. We shouldn't relay requests here either, as we might miss our + * chance to create a host route. + */ + if (host && host->cleanup_pending) + return; + + relay_arp_request(rif, pkt); +} + +static void recv_arp_reply(struct relayd_interface *rif, struct arp_packet *pkt) +{ + struct relayd_host *host; + + DPRINTF(2, "%s: received ARP reply for "IP_FMT" from "MAC_FMT", deliver to "IP_FMT"\n", + rif->ifname, + IP_BUF(pkt->arp.arp_spa), + MAC_BUF(pkt->eth.ether_shost), + IP_BUF(pkt->arp.arp_tpa)); + + if (memcmp(pkt->arp.arp_sha, rif->sll.sll_addr, ETH_ALEN) != 0) + relayd_refresh_host(rif, pkt->arp.arp_sha, pkt->arp.arp_spa); + + host = find_host_by_ipaddr(NULL, pkt->arp.arp_tpa); + if (!host) + return; + + if (host->rif == rif) + return; + + send_arp_reply(host->rif, pkt->arp.arp_spa, host->lladdr, host->ipaddr); +} + +static void recv_packet(struct uloop_fd *fd, unsigned int events) +{ + struct relayd_interface *rif = container_of(fd, struct relayd_interface, fd); + struct arp_packet *pkt; + static char pktbuf[4096]; + int pktlen; + + do { + if (rif->fd.error) + uloop_end(); + + pktlen = recv(rif->fd.fd, pktbuf, sizeof(pktbuf), 0); + if (pktlen < 0) { + if (errno == EINTR) + continue; + + break; + } + + if (!pktlen) + break; + + pkt = (void *)pktbuf; + if (pkt->arp.arp_op == htons(ARPOP_REPLY)) + recv_arp_reply(rif, pkt); + else if (pkt->arp.arp_op == htons(ARPOP_REQUEST)) + recv_arp_request(rif, pkt); + else + DPRINTF(1, "received unknown packet type: %04x\n", ntohs(pkt->arp.arp_op)); + + } while (1); +} + +void relayd_forward_bcast_packet(struct relayd_interface *from_rif, void *packet, int len) +{ + struct relayd_interface *rif; + struct ether_header *eth = packet; + + list_for_each_entry(rif, &interfaces, list) { + if (rif == from_rif) + continue; + + DPRINTF(3, "%s: forwarding broadcast packet to %s\n", from_rif->ifname, rif->ifname); + memcpy(eth->ether_shost, rif->sll.sll_addr, ETH_ALEN); + send(rif->bcast_fd.fd, packet, len, 0); + } +} + +static void recv_bcast_packet(struct uloop_fd *fd, unsigned int events) +{ + struct relayd_interface *rif = container_of(fd, struct relayd_interface, bcast_fd); + static char pktbuf[4096]; + int pktlen; + + do { + if (rif->fd.error) + uloop_end(); + + pktlen = recv(rif->bcast_fd.fd, pktbuf, sizeof(pktbuf), 0); + if (pktlen < 0) { + if (errno == EINTR) + continue; + + break; + } + + if (!pktlen) + break; + + if (!forward_bcast && !forward_dhcp) + continue; + + if (relayd_handle_dhcp_packet(rif, pktbuf, pktlen, forward_dhcp, parse_dhcp)) + continue; + + if (forward_bcast) + relayd_forward_bcast_packet(rif, pktbuf, pktlen); + } while (1); +} + + +static int init_interface(struct relayd_interface *rif) +{ + struct sockaddr_ll *sll = &rif->sll; + struct sockaddr_in *sin; + struct ifreq ifr; + int fd = rif->fd.fd; + const int one = 1; +#ifdef PACKET_RECV_TYPE + unsigned int pkt_type; +#endif + + fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP)); + if (fd < 0) + return -1; + + rif->fd.fd = fd; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, rif->ifname); + + if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) { + perror("ioctl(SIOCGIFHWADDR)"); + return -1; + } + + memcpy(sll->sll_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + sll->sll_family = AF_PACKET; + sll->sll_protocol = htons(ETH_P_ARP); + sll->sll_pkttype = PACKET_BROADCAST; + sll->sll_hatype = ARPHRD_ETHER; + sll->sll_halen = ETH_ALEN; + + if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { + perror("ioctl(SIOCGIFINDEX)"); + return -1; + } + + sll->sll_ifindex = ifr.ifr_ifindex; + + if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) { + memcpy(rif->src_ip, DUMMY_IP, sizeof(rif->src_ip)); + } else { + sin = (struct sockaddr_in *) &ifr.ifr_addr; + memcpy(rif->src_ip, &sin->sin_addr.s_addr, sizeof(rif->src_ip)); + } +/* starts here */ + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) { + perror("setsockopt(ETH_P_ARP)"); + } +/* ends here */ + if (bind(fd, (struct sockaddr *)sll, sizeof(struct sockaddr_ll)) < 0) { + perror("bind(ETH_P_ARP)"); + return -1; + } + + rif->fd.cb = recv_packet; + uloop_fd_add(&rif->fd, ULOOP_READ | ULOOP_EDGE_TRIGGER); + + if (!forward_bcast && !forward_dhcp) + return 0; + + fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IP)); + if (fd < 0) + return 0; + + rif->bcast_fd.fd = fd; + rif->bcast_fd.cb = recv_bcast_packet; + + memcpy(&rif->bcast_sll, &rif->sll, sizeof(rif->bcast_sll)); + sll = &rif->bcast_sll; + sll->sll_protocol = htons(ETH_P_IP); + +/* starts here */ + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) { + perror("setsockopt(ETH_P_IP)"); + } +/* ends here */ + + if (bind(fd, (struct sockaddr *)sll, sizeof(struct sockaddr_ll)) < 0) { + perror("bind(ETH_P_IP)"); + return 0; + } + +#ifdef PACKET_RECV_TYPE + pkt_type = (1 << PACKET_BROADCAST) | (1 << PACKET_MULTICAST); + setsockopt(fd, SOL_PACKET, PACKET_RECV_TYPE, &pkt_type, sizeof(pkt_type)); +#endif + + uloop_fd_add(&rif->bcast_fd, ULOOP_READ | ULOOP_EDGE_TRIGGER); + relayd_add_interface_routes(rif); + return 0; +} + +static void ping_static_routes(void) +{ + struct relayd_pending_route *rt; + struct relayd_interface *rif; + + list_for_each_entry(rt, &pending_routes, rt.list) + list_for_each_entry(rif, &interfaces, list) + send_arp_request(rif, rt->gateway); +} + +static int init_interfaces(void) +{ + struct relayd_interface *rif; + int ret; + + list_for_each_entry(rif, &interfaces, list) { + ret = init_interface(rif); + if (ret < 0) + return ret; + } + + return 0; +} + +static void cleanup_hosts(void) +{ + struct relayd_interface *rif; + struct relayd_host *host, *tmp; + + list_for_each_entry(rif, &interfaces, list) { + list_for_each_entry_safe(host, tmp, &rif->hosts, list) { + del_host(host); + } + } +} + +static void free_interfaces(void) +{ + struct relayd_interface *rif, *rtmp; + + list_for_each_entry_safe(rif, rtmp, &interfaces, list) { + relayd_del_interface_routes(rif); + list_del(&rif->list); + free(rif); + } +} + +static struct relayd_interface *alloc_interface(const char *ifname, bool managed) +{ + struct relayd_interface *rif; + + if (strlen(ifname) >= IFNAMSIZ) + return NULL; + + rif = calloc(1, sizeof(*rif)); + if (!rif) + return NULL; + + INIT_LIST_HEAD(&rif->hosts); + strcpy(rif->ifname, ifname); + list_add(&rif->list, &interfaces); + rif->managed = managed; + + return rif; +} + +static void die(int signo) +{ + /* + * When we hit SIGTERM, clean up interfaces directly, so that we + * won't leave our routing in an invalid state. + */ + uloop_end(); +} + +static int usage(const char *progname) +{ + fprintf(stderr, "Usage: %s \n" + "\n" + "Options:\n" + " -d Enable debug messages\n" + " -i Add an interface for relaying\n" + " -I Same as -i, except with ARP cache and host route management\n" + " You need to specify at least two interfaces\n" + " -G Set a gateway IP for clients\n" + " -R :/\n" + " Add a static route for / via \n" + " -t Host entry expiry timeout\n" + " -p Number of ARP ping attempts before considering a host dead\n" + " -T Set routing table number for automatically added routes\n" + " -B Enable broadcast forwarding\n" + " -D Enable DHCP forwarding\n" + " -P Disable DHCP options parsing\n" + " -L Enable local access using as source address\n" + " this is edited on 22.04.2015 " + "\n", + progname); + return -1; +} + +int main(int argc, char **argv) +{ + struct relayd_interface *rif = NULL; + struct in_addr addr, addr2; + bool local_addr_valid = false; + bool managed; + int ifnum = 0; + char *s, *s2; + int mask; + int ch; + + debug = 0; + inet_sock = socket(AF_INET, SOCK_DGRAM, 0); + if (inet_sock < 0) { + perror("socket(AF_INET)"); + return 1; + } + + host_timeout = 30; + host_ping_tries = 5; + forward_bcast = 0; + local_route_table = 0; + parse_dhcp = 1; + uloop_init(); + + while ((ch = getopt(argc, argv, "I:i:t:p:BDPdT:G:R:L:")) != -1) { + switch(ch) { + case 'I': + managed = true; + /* fall through */ + case 'i': + ifnum++; + rif = alloc_interface(optarg, managed); + if (!rif) + return 1; + + managed = false; + break; + case 't': + host_timeout = atoi(optarg); + if (host_timeout <= 0) + return usage(argv[0]); + break; + case 'p': + host_ping_tries = atoi(optarg); + if (host_ping_tries <= 0) + return usage(argv[0]); + break; + case 'd': + debug++; + break; + case 'B': + forward_bcast = 1; + break; + case 'D': + forward_dhcp = 1; + break; + case 'P': + parse_dhcp = 0; + break; + case 'T': + route_table = atoi(optarg); + if (route_table <= 0) + return usage(argv[0]); + break; + case 'G': + if (!inet_aton(optarg, &addr)) { + fprintf(stderr, "Address '%s' not found\n", optarg); + return 1; + } + relayd_add_pending_route((uint8_t *) &addr.s_addr, (const uint8_t *) "\x00\x00\x00\x00", 0, 0); + break; + case 'L': + if (!inet_aton(optarg, &addr)) { + fprintf(stderr, "Address '%s' not found\n", optarg); + return 1; + } + memcpy(&local_addr, &addr.s_addr, sizeof(local_addr)); + local_addr_valid = true; + break; + case 'R': + s = strchr(optarg, ':'); + if (!s) + return usage(argv[0]); + + *(s++) = 0; + if (!inet_aton(optarg, &addr)) { + fprintf(stderr, "Address '%s' not found\n", optarg); + return 1; + } + + s2 = strchr(s, '/'); + if (!s2) + return usage(argv[0]); + + *(s2++) = 0; + if (!inet_aton(s, &addr2)) { + fprintf(stderr, "Address '%s' not found\n", s); + return 1; + } + + mask = atoi(s2); + if (mask < 0 || mask > 32) + return usage(argv[0]); + + relayd_add_pending_route((uint8_t *) &addr.s_addr, (uint8_t *) &addr2.s_addr, mask, 0); + break; + case '?': + default: + return usage(argv[0]); + } + } + + if (list_empty(&interfaces)) + return usage(argv[0]); + + if (ifnum < 2) { + fprintf(stderr, "ERROR: Need at least 2 interfaces for relaying\n"); + return -1; + } + + argc -= optind; + argv += optind; + + signal(SIGTERM, die); + signal(SIGHUP, die); + signal(SIGUSR1, die); + signal(SIGUSR2, die); + + if (local_addr_valid) + local_route_table = route_table++; + + if (relayd_rtnl_init() < 0) + return 1; + + if (init_interfaces() < 0) + return 1; + + ping_static_routes(); + + uloop_run(); + uloop_done(); + + cleanup_hosts(); + free_interfaces(); + relayd_rtnl_done(); + close(inet_sock); + + return 0; +} diff --git a/relayd.h b/relayd.h new file mode 100644 index 0000000..d7ad212 --- /dev/null +++ b/relayd.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2010 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + */ +#ifndef __RELAYD_H +#define __RELAYD_H + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#define DEBUG +#ifdef DEBUG +#define DPRINTF(level, ...) if (debug >= level) fprintf(stderr, __VA_ARGS__); +#else +#define DPRINTF(...) do {} while(0) +#endif + +#ifndef __packed +#define __packed __attribute__((packed)) +#endif + +#define __uc(c) ((unsigned char *)(c)) + +#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x" +#define MAC_BUF(_c) __uc(_c)[0], __uc(_c)[1], __uc(_c)[2], __uc(_c)[3], __uc(_c)[4], __uc(_c)[5] + +#define IP_FMT "%d.%d.%d.%d" +#define IP_BUF(_c) __uc(_c)[0], __uc(_c)[1], __uc(_c)[2], __uc(_c)[3] + +#define DUMMY_IP ((uint8_t *) "\x01\x01\x01\x01") + +#define DHCP_FLAG_BROADCAST (1 << 15) + +struct relayd_interface { + struct list_head list; + struct uloop_fd fd; + struct uloop_fd bcast_fd; + struct sockaddr_ll sll; + struct sockaddr_ll bcast_sll; + char ifname[IFNAMSIZ]; + struct list_head hosts; + uint8_t src_ip[4]; + bool managed; + int rt_table; +}; + +struct relayd_host { + struct list_head list; + struct list_head routes; + struct relayd_interface *rif; + uint8_t lladdr[ETH_ALEN]; + uint8_t ipaddr[4]; + struct uloop_timeout timeout; + int cleanup_pending; +}; + +struct relayd_route { + struct list_head list; + uint8_t dest[4]; + uint8_t mask; +}; + +struct arp_packet { + struct ether_header eth; + struct ether_arp arp; +} __packed; + +struct rtnl_req { + struct nlmsghdr nl; + struct rtmsg rt; +} __packed; + +extern struct list_head interfaces; +extern int debug; +extern int route_table; +extern uint8_t local_addr[4]; +extern int local_route_table; + +void rtnl_route_set(struct relayd_host *host, struct relayd_route *route, bool add); + +static inline void relayd_add_route(struct relayd_host *host, struct relayd_route *route) +{ + rtnl_route_set(host, route, true); +} + +static inline void relayd_del_route(struct relayd_host *host, struct relayd_route *route) +{ + rtnl_route_set(host, route, false); +} + +void relayd_add_interface_routes(struct relayd_interface *rif); +void relayd_del_interface_routes(struct relayd_interface *rif); + +int relayd_rtnl_init(void); +void relayd_rtnl_done(void); + +struct relayd_host *relayd_refresh_host(struct relayd_interface *rif, + const uint8_t *lladdr, + const uint8_t *ipaddr); +void relayd_add_host_route(struct relayd_host *host, const uint8_t *ipaddr, uint8_t mask); +void relayd_add_pending_route(const uint8_t *gateway, const uint8_t *dest, uint8_t mask, int timeout); + +void relayd_forward_bcast_packet(struct relayd_interface *from_rif, void *packet, int len); +bool relayd_handle_dhcp_packet(struct relayd_interface *rif, void *data, int len, bool forward, bool parse); + +#endif diff --git a/route.c b/route.c new file mode 100644 index 0000000..a5676d6 --- /dev/null +++ b/route.c @@ -0,0 +1,416 @@ +/* + * Copyright (C) 2010 Felix Fietkau + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License v2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "relayd.h" + +static struct uloop_fd rtnl_sock; +static unsigned int rtnl_seq, rtnl_dump_seq; +int route_table = 16800; + +static void rtnl_flush(void) +{ + int fd; + + fd = open("/proc/sys/net/ipv4/route/flush", O_WRONLY); + if (fd < 0) + return; + + write(fd, "-1", 2); + close(fd); +} + +enum { + RULE_F_ADD = (1 << 0), + RULE_F_DEFGW_WORKAROUND = (1 << 1), +}; + +static int get_route_table(struct relayd_interface *rif) +{ + if (rif) + return rif->rt_table; + else + return local_route_table; +} + +static void +rtnl_rule_request(struct relayd_interface *rif, int flags) +{ + static struct { + struct nlmsghdr nl; + struct rtmsg rt; + struct { + struct rtattr rta; + int table; + } __packed table; + struct { + struct rtattr rta; + char ifname[IFNAMSIZ + 1]; + } __packed dev; + } __packed req = { + .rt = { + .rtm_family = AF_INET, + .rtm_table = RT_TABLE_UNSPEC, + .rtm_scope = RT_SCOPE_UNIVERSE, + .rtm_protocol = RTPROT_BOOT, + }, + .table.rta = { + .rta_type = FRA_TABLE, + .rta_len = sizeof(req.table), + }, + }; + const char *ifname = "lo"; + int padding = sizeof(req.dev.ifname); + + if (rif) + ifname = rif->ifname; + + if (!(flags & RULE_F_DEFGW_WORKAROUND)) { + req.dev.rta.rta_type = FRA_IFNAME; + padding -= strlen(ifname) + 1; + strcpy(req.dev.ifname, ifname); + req.dev.rta.rta_len = sizeof(req.dev.rta) + strlen(ifname) + 1; + } else { + uint32_t val = 1; + req.dev.rta.rta_type = FRA_PRIORITY; + req.dev.rta.rta_len = sizeof(req.dev.rta) + sizeof(uint32_t); + padding -= sizeof(uint32_t); + memcpy(&req.dev.ifname, &val, sizeof(val)); + } + req.table.table = get_route_table(rif); + req.nl.nlmsg_len = sizeof(req) - padding; + + req.nl.nlmsg_flags = NLM_F_REQUEST; + if (flags & RULE_F_ADD) { + req.nl.nlmsg_type = RTM_NEWRULE; + req.nl.nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; + + req.rt.rtm_type = RTN_UNICAST; + } else { + req.nl.nlmsg_type = RTM_DELRULE; + req.rt.rtm_type = RTN_UNSPEC; + } + + send(rtnl_sock.fd, &req, req.nl.nlmsg_len, 0); + rtnl_flush(); +} + +struct rtnl_addr { + struct rtattr rta; + uint8_t ipaddr[4]; +} __packed; + +static struct rtnl_addr * +rtnl_add_addr(struct rtnl_addr *addr, int *len, int type, const uint8_t *ipaddr) +{ + addr->rta.rta_type = type; + memcpy(addr->ipaddr, ipaddr, 4); + *len += sizeof(*addr); + return addr + 1; +} + +static void +rtnl_route_request(struct relayd_interface *rif, struct relayd_host *host, + struct relayd_route *route, bool add) +{ + static struct { + struct nlmsghdr nl; + struct rtmsg rt; + struct { + struct rtattr rta; + int table; + } __packed table; + struct { + struct rtattr rta; + int ifindex; + } __packed dev; + struct rtnl_addr addr[3]; + } __packed req = { + .rt = { + .rtm_family = AF_INET, + .rtm_dst_len = 32, + .rtm_table = RT_TABLE_MAIN, + }, + .table.rta = { + .rta_type = RTA_TABLE, + .rta_len = sizeof(req.table), + }, + .dev.rta = { + .rta_type = RTA_OIF, + .rta_len = sizeof(req.dev), + }, + .addr[0].rta.rta_len = sizeof(struct rtnl_addr), + .addr[1].rta.rta_len = sizeof(struct rtnl_addr), + .addr[2].rta.rta_len = sizeof(struct rtnl_addr), + }; + int pktlen = sizeof(req) - sizeof(req.addr); + struct rtnl_addr *addr = &req.addr[0]; + const char *ifname = "loopback"; + + req.dev.ifindex = host->rif->sll.sll_ifindex; + req.table.table = get_route_table(rif); + + req.nl.nlmsg_flags = NLM_F_REQUEST; + if (add) { + req.nl.nlmsg_type = RTM_NEWROUTE; + req.nl.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE; + + req.rt.rtm_protocol = RTPROT_BOOT; + if (route) { + req.rt.rtm_scope = RT_SCOPE_UNIVERSE; + } else { + req.rt.rtm_scope = RT_SCOPE_LINK; + } + req.rt.rtm_type = RTN_UNICAST; + } else { + req.nl.nlmsg_type = RTM_DELROUTE; + req.rt.rtm_scope = RT_SCOPE_NOWHERE; + } + + if (rif) + ifname = rif->ifname; + + if (route) { + DPRINTF(2, "%s: add route to "IP_FMT"/%d via "IP_FMT" (%s)\n", ifname, + IP_BUF(route->dest), route->mask, IP_BUF(host->ipaddr), + host->rif->ifname); + + req.rt.rtm_dst_len = route->mask; + if (route->mask) + addr = rtnl_add_addr(addr, &pktlen, RTA_DST, route->dest); + addr = rtnl_add_addr(addr, &pktlen, RTA_GATEWAY, host->ipaddr); + } else { + DPRINTF(2, "%s: add host route to "IP_FMT" (%s)\n", ifname, + IP_BUF(host->ipaddr), host->rif->ifname); + addr = rtnl_add_addr(addr, &pktlen, RTA_DST, host->ipaddr); + req.rt.rtm_dst_len = 32; + } + + /* local route */ + if (!rif) + addr = rtnl_add_addr(addr, &pktlen, RTA_PREFSRC, local_addr); + + req.nl.nlmsg_len = pktlen; + if (route) + rtnl_rule_request(rif, RULE_F_DEFGW_WORKAROUND | RULE_F_ADD); + send(rtnl_sock.fd, &req, pktlen, 0); + if (route) + rtnl_rule_request(rif, RULE_F_DEFGW_WORKAROUND); + rtnl_flush(); +} + +void +rtnl_route_set(struct relayd_host *host, struct relayd_route *route, bool add) +{ + struct relayd_interface *rif; + + list_for_each_entry(rif, &interfaces, list) { + if (rif == host->rif) + continue; + + rtnl_route_request(rif, host, route, add); + } + if (local_route_table) + rtnl_route_request(NULL, host, route, add); +} + +void relayd_add_interface_routes(struct relayd_interface *rif) +{ + rif->rt_table = route_table++; + rtnl_rule_request(rif, RULE_F_ADD); +} + +void relayd_del_interface_routes(struct relayd_interface *rif) +{ + rtnl_rule_request(rif, 0); +} + +#ifndef NDA_RTA +#define NDA_RTA(r) \ + ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg)))) +#endif + +static void rtnl_parse_newneigh(struct nlmsghdr *h) +{ + struct relayd_interface *rif = NULL; + struct ndmsg *r = NLMSG_DATA(h); + const uint8_t *lladdr = NULL; + const uint8_t *ipaddr = NULL; + struct rtattr *rta; + int len; + + if (r->ndm_family != AF_INET) + return; + + list_for_each_entry(rif, &interfaces, list) { + if (rif->sll.sll_ifindex == r->ndm_ifindex) + goto found_interface; + } + return; + +found_interface: + len = h->nlmsg_len - NLMSG_LENGTH(sizeof(*r)); + for (rta = NDA_RTA(r); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) { + switch(rta->rta_type) { + case NDA_LLADDR: + lladdr = RTA_DATA(rta); + break; + case NDA_DST: + ipaddr = RTA_DATA(rta); + break; + default: + break; + } + } + + if (!lladdr || !ipaddr || (r->ndm_state & (NUD_INCOMPLETE|NUD_FAILED))) + return; + + if (!memcmp(lladdr, "\x00\x00\x00\x00\x00\x00", ETH_ALEN)) + return; + + DPRINTF(1, "%s: Found ARP cache entry for host "IP_FMT" ("MAC_FMT")\n", + rif->ifname, IP_BUF(ipaddr), MAC_BUF(lladdr)); + relayd_refresh_host(rif, lladdr, ipaddr); +} + +static void rtnl_parse_packet(void *data, int len) +{ + struct nlmsghdr *h; + + for (h = data; NLMSG_OK(h, len); h = NLMSG_NEXT(h, len)) { + if (h->nlmsg_type == NLMSG_DONE || + h->nlmsg_type == NLMSG_ERROR) + return; + + if (h->nlmsg_seq != rtnl_dump_seq) + continue; + + if (h->nlmsg_type == RTM_NEWNEIGH) + rtnl_parse_newneigh(h); + } +} + +static void rtnl_cb(struct uloop_fd *fd, unsigned int events) +{ + struct sockaddr_nl nladdr; + static uint8_t buf[16384]; + struct iovec iov = { + .iov_base = buf, + .iov_len = sizeof(buf), + }; + struct msghdr msg = { + .msg_name = &nladdr, + .msg_namelen = sizeof(nladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + + do { + int len; + + len = recvmsg(rtnl_sock.fd, &msg, 0); + if (len < 0) { + if (errno == EINTR) + continue; + + return; + } + + if (!len) + break; + + if (nladdr.nl_pid != 0) + continue; + + rtnl_parse_packet(buf, len); + } while (1); +} + +static void rtnl_dump_request(int nlmsg_type) +{ + static struct { + struct nlmsghdr nlh; + struct rtgenmsg g; + } req = { + .nlh = { + .nlmsg_len = sizeof(req), + .nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST, + .nlmsg_pid = 0, + }, + .g.rtgen_family = AF_INET, + }; + req.nlh.nlmsg_type = nlmsg_type; + req.nlh.nlmsg_seq = rtnl_seq; + send(rtnl_sock.fd, &req, sizeof(req), 0); + rtnl_seq++; +} + +int relayd_rtnl_init(void) +{ + struct sockaddr_nl snl_local; + const int one = 1; + + rtnl_sock.fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (rtnl_sock.fd < 0) { + perror("socket(AF_NETLINK)"); + return -1; + } + + snl_local.nl_family = AF_NETLINK; + + /*START: MUSTAFA: CHANGES TO "RESOLVE ADDRESS ALREADY IN USE" ISSUE */ + printf("setting nl_pid = %d\n",getpid()); + snl_local.nl_pid = getpid(); + + if(setsockopt(rtnl_sock.fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) { + perror("setsockopt(AF_NETLINK:rtnl_sock.fd)"); + } + /*END: MUSTAFA: CHANGES TO "RESOLVE ADDRESS ALREADY IN USE" ISSUE */ + + if (bind(rtnl_sock.fd, (struct sockaddr *) &snl_local, sizeof(struct sockaddr_nl)) < 0) { + perror("PID:::AF_NETLINK:rtnl_sock.fd bind"); + close(rtnl_sock.fd); + return -1; + } + + rtnl_sock.cb = rtnl_cb; + uloop_fd_add(&rtnl_sock, ULOOP_READ | ULOOP_EDGE_TRIGGER); + + rtnl_seq = time(NULL); + rtnl_dump_seq = rtnl_seq; + rtnl_dump_request(RTM_GETNEIGH); + rtnl_rule_request(NULL, RULE_F_ADD); + + return 0; +} + +void relayd_rtnl_done(void) +{ + rtnl_rule_request(NULL, 0); + uloop_fd_delete(&rtnl_sock); + close(rtnl_sock.fd); +}