summaryrefslogtreecommitdiffstats
path: root/if-linux.c
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2009-01-27 18:09:02 +0000
committerRoy Marples <roy@marples.name>2009-01-27 18:09:02 +0000
commitfa344ba2b1dcf002f348b7fa69ef422585b4b438 (patch)
tree0a20b9e8aeec52fcec0f5c30a669cb8b444b35ec /if-linux.c
parent16687a3be1f8c1e6d55cdd691e947e5760eef30c (diff)
downloaddhcpcd-fa344ba2b1dcf002f348b7fa69ef422585b4b438.tar.xz
Add route removal detection to Linux.
This involved a rejig of the socket code so we can detect if we removed the route or not.
Diffstat (limited to 'if-linux.c')
-rw-r--r--if-linux.c112
1 files changed, 90 insertions, 22 deletions
diff --git a/if-linux.c b/if-linux.c
index 6763c2b8..71723e92 100644
--- a/if-linux.c
+++ b/if-linux.c
@@ -66,6 +66,7 @@
#include "config.h"
#include "common.h"
+#include "configure.h"
#include "dhcp.h"
#include "net.h"
@@ -75,6 +76,9 @@ static void (*nl_carrier)(const char *);
static void (*nl_add)(const char *);
static void (*nl_remove)(const char *);
+static int sock_fd;
+static struct sockaddr_nl sock_nl;
+
int
getifssid(const char *ifname, char *ssid)
{
@@ -96,23 +100,37 @@ getifssid(const char *ifname, char *ssid)
return retval;
}
-int
-open_link_socket(void)
+static int
+_open_link_socket(struct sockaddr_nl *nl)
{
int fd;
- struct sockaddr_nl nl;
if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1)
return -1;
- memset(&nl, 0, sizeof(nl));
- nl.nl_family = AF_NETLINK;
- nl.nl_groups = RTMGRP_LINK;
- if (bind(fd, (struct sockaddr *)&nl, sizeof(nl)) == -1)
+ nl->nl_family = AF_NETLINK;
+ if (bind(fd, (struct sockaddr *)nl, sizeof(*nl)) == -1)
return -1;
set_cloexec(fd);
return fd;
}
+int
+init_socket(void)
+{
+ sock_fd = _open_link_socket(&sock_nl);
+ return sock_fd;
+}
+
+int
+open_link_socket(void)
+{
+ struct sockaddr_nl snl;
+
+ memset(&snl, 0, sizeof(snl));
+ snl.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE;
+ return _open_link_socket(&snl);
+}
+
static int
get_netlink(int fd, int flags,
int (*callback)(struct nlmsghdr *))
@@ -170,6 +188,63 @@ err_netlink(struct nlmsghdr *nlm)
}
static int
+link_delroute(struct nlmsghdr *nlm)
+{
+ int len, idx, metric;
+ struct rtattr *rta;
+ struct rtmsg *rtm;
+ struct rt rt;
+ char ifn[IF_NAMESIZE + 1];
+
+ len = nlm->nlmsg_len - sizeof(*nlm);
+ if ((size_t)len < sizeof(*rtm)) {
+ errno = EBADMSG;
+ return -1;
+ }
+ rtm = NLMSG_DATA(nlm);
+ if (rtm->rtm_type != RTN_UNICAST ||
+ rtm->rtm_table != RT_TABLE_MAIN ||
+ rtm->rtm_family != AF_INET ||
+ nlm->nlmsg_pid == (uint32_t)getpid())
+ return 1;
+ rta = (struct rtattr *) ((char *)rtm + NLMSG_ALIGN(sizeof(*rtm)));
+ len = NLMSG_PAYLOAD(nlm, sizeof(*rtm));
+ rt.iface = NULL;
+ rt.next = NULL;
+ metric = 0;
+ while (RTA_OK(rta, len)) {
+ switch (rta->rta_type) {
+ case RTA_DST:
+ memcpy(&rt.dest.s_addr, RTA_DATA(rta),
+ sizeof(rt.dest.s_addr));
+ break;
+ case RTA_GATEWAY:
+ memcpy(&rt.gate.s_addr, RTA_DATA(rta),
+ sizeof(rt.gate.s_addr));
+ break;
+ case RTA_OIF:
+ idx = *(int *)RTA_DATA(rta);
+ if (if_indextoname(idx, ifn))
+ rt.iface = find_interface(ifn);
+ break;
+ case RTA_PRIORITY:
+ metric = *(int *)RTA_DATA(rta);
+ break;
+ }
+ rta = RTA_NEXT(rta, len);
+ }
+ if (rt.iface != NULL) {
+ if (metric == rt.iface->metric) {
+ if (rt.dest.s_addr == INADDR_BROADCAST)
+ rt.dest.s_addr = INADDR_ANY;
+ inet_cidrtoaddr(rtm->rtm_dst_len, &rt.net);
+ route_deleted(&rt);
+ }
+ }
+ return 1;
+}
+
+static int
link_netlink(struct nlmsghdr *nlm)
{
int len;
@@ -177,6 +252,9 @@ link_netlink(struct nlmsghdr *nlm)
struct ifinfomsg *ifi;
char ifn[IF_NAMESIZE + 1];
+ if (nlm->nlmsg_type == RTM_DELROUTE)
+ return link_delroute(nlm);
+
if (nlm->nlmsg_type != RTM_NEWLINK && nlm->nlmsg_type != RTM_DELLINK)
return 0;
len = nlm->nlmsg_len - sizeof(*nlm);
@@ -234,37 +312,27 @@ manage_link(int fd,
static int
send_netlink(struct nlmsghdr *hdr)
{
- int fd, r;
- struct sockaddr_nl nl;
+ int r;
struct iovec iov;
struct msghdr msg;
static unsigned int seq;
- if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1)
- return -1;
- memset(&nl, 0, sizeof(nl));
- nl.nl_family = AF_NETLINK;
- if (bind(fd, (struct sockaddr *)&nl, sizeof(nl)) == -1) {
- close(fd);
- return -1;
- }
memset(&iov, 0, sizeof(iov));
iov.iov_base = hdr;
iov.iov_len = hdr->nlmsg_len;
memset(&msg, 0, sizeof(msg));
- msg.msg_name = &nl;
- msg.msg_namelen = sizeof(nl);
+ msg.msg_name = &sock_nl;
+ msg.msg_namelen = sizeof(sock_nl);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
/* Request a reply */
hdr->nlmsg_flags |= NLM_F_ACK;
hdr->nlmsg_seq = ++seq;
- if (sendmsg(fd, &msg, 0) != -1)
- r = get_netlink(fd, 0, &err_netlink);
+ if (sendmsg(sock_fd, &msg, 0) != -1)
+ r = get_netlink(sock_fd, 0, &err_netlink);
else
r = -1;
- close(fd);
return r;
}