aboutsummaryrefslogtreecommitdiffstats
path: root/pingu_netlink.c
diff options
context:
space:
mode:
Diffstat (limited to 'pingu_netlink.c')
-rw-r--r--pingu_netlink.c138
1 files changed, 89 insertions, 49 deletions
diff --git a/pingu_netlink.c b/pingu_netlink.c
index 9e670ed..3ee0745 100644
--- a/pingu_netlink.c
+++ b/pingu_netlink.c
@@ -104,6 +104,20 @@ static int netlink_add_rtattr_l(struct nlmsghdr *n, int maxlen, int type,
return TRUE;
}
+static int netlink_add_rtattr_addr_any(struct nlmsghdr *n, int maxlen,
+ int type, union sockaddr_any *sa)
+{
+ switch (sa->sa.sa_family) {
+ case AF_INET:
+ return netlink_add_rtattr_l(n, maxlen, type, &sa->sin.sin_addr, 4);
+ break;
+ case AF_INET6:
+ return netlink_add_rtattr_l(n, maxlen, type, &sa->sin6.sin6_addr, 16);
+ break;
+ }
+ return FALSE;
+}
+
static int netlink_receive(struct netlink_fd *fd, struct nlmsghdr *reply)
{
struct sockaddr_nl nladdr;
@@ -186,9 +200,9 @@ static int netlink_enumerate(struct netlink_fd *fd, int family, int type)
(struct sockaddr *) &addr, sizeof(addr)) >= 0;
}
-int netlink_route_modify(struct netlink_fd *fd, int type,
- in_addr_t destination, uint32_t masklen, in_addr_t gateway,
- uint32_t metric, int iface_index, int table)
+int netlink_route_modify(struct netlink_fd *fd, int action_type,
+ struct pingu_gateway *route,
+ int iface_index, int table)
{
struct {
struct nlmsghdr nlh;
@@ -203,42 +217,42 @@ int netlink_route_modify(struct netlink_fd *fd, int type,
req.nlh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
req.nlh.nlmsg_flags = NLM_F_REQUEST;
- req.nlh.nlmsg_type = type;
- if (type == RTM_NEWROUTE)
+ req.nlh.nlmsg_type = action_type;
+ if (action_type == RTM_NEWROUTE)
req.nlh.nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
- req.msg.rtm_family = AF_INET;
+ req.msg.rtm_family = route->dest.sa.sa_family;
req.msg.rtm_table = table;
- req.msg.rtm_dst_len = masklen;
+ req.msg.rtm_dst_len = route->dest_len;
req.msg.rtm_protocol = RTPROT_BOOT;
req.msg.rtm_scope = RT_SCOPE_UNIVERSE;
req.msg.rtm_type = RTN_UNICAST;
- netlink_add_rtattr_l(&req.nlh, sizeof(req), RTA_DST, &destination, 4);
+ netlink_add_rtattr_addr_any(&req.nlh, sizeof(req), RTA_DST,
+ &route->dest);
+ netlink_add_rtattr_addr_any(&req.nlh, sizeof(req), RTA_GATEWAY,
+ &route->gw_addr);
netlink_add_rtattr_l(&req.nlh, sizeof(req), RTA_OIF, &iface_index, 4);
- netlink_add_rtattr_l(&req.nlh, sizeof(req), RTA_GATEWAY, &gateway, 4);
- if (metric != 0)
- netlink_add_rtattr_l(&req.nlh, sizeof(req), RTA_PRIORITY, &metric, 4);
+ if (route->metric != 0)
+ netlink_add_rtattr_l(&req.nlh, sizeof(req), RTA_PRIORITY,
+ &route->metric, 4);
return sendto(fd->fd, (void *) &req, sizeof(req), 0,
(struct sockaddr *) &addr, sizeof(addr));
-
}
-int netlink_route_replace_or_add(struct netlink_fd *fd,
- in_addr_t destination, uint32_t masklen, in_addr_t gateway,
- uint32_t metric, int iface_index, int table)
+int netlink_route_replace_or_add(struct netlink_fd *fd,
+ struct pingu_gateway *route,
+ int iface_index, int table)
{
- return netlink_route_modify(fd, RTM_NEWROUTE, destination, masklen,
- gateway, metric, iface_index, table);
+ return netlink_route_modify(fd, RTM_NEWROUTE, route, iface_index, table);
}
int netlink_route_delete(struct netlink_fd *fd,
- in_addr_t destination, uint32_t masklen, in_addr_t gateway,
- uint32_t metric, int iface_index, int table)
+ struct pingu_gateway *route,
+ int iface_index, int table)
{
- return netlink_route_modify(fd, RTM_DELROUTE, destination, masklen,
- gateway, metric, iface_index, table);
+ return netlink_route_modify(fd, RTM_DELROUTE, route, iface_index, table);
}
int netlink_rule_modify(struct netlink_fd *fd,
@@ -383,54 +397,80 @@ static void netlink_addr_del_cb(struct nlmsghdr *nlmsg)
pingu_iface_set_addr(iface, 0, NULL, 0);
}
-static void netlink_route_cb_action(struct nlmsghdr *msg, int action)
+static struct pingu_gateway *gw_from_rtmsg(struct pingu_gateway *gw,
+ struct rtmsg *rtm,
+ struct rtattr **rta)
{
- struct pingu_iface *iface;
- struct rtmsg *rtm = NLMSG_DATA(msg);
- struct rtattr *rta[RTA_MAX+1];
+ memset(gw, 0, sizeof(*gw));
+ gw->dest_len = rtm->rtm_dst_len;
+ gw->dest.sa.sa_family = rtm->rtm_family;
+ if (rta[RTA_DST] != NULL)
+ sockaddr_init(&gw->dest, rtm->rtm_family, RTA_DATA(rta[RTA_DST]));
+
+ if (rta[RTA_PRIORITY] != NULL)
+ gw->metric = *(uint32_t *)RTA_DATA(rta[RTA_PRIORITY]);
+
+ if (rta[RTA_GATEWAY] != NULL)
+ sockaddr_init(&gw->gw_addr, rtm->rtm_family, RTA_DATA(rta[RTA_GATEWAY]));
+ return gw;
+}
- in_addr_t destination = 0;
- in_addr_t gateway = 0;
- uint32_t metric = 0;
+static void log_route_change(struct pingu_gateway *route,
+ struct pingu_iface *iface, int action)
+{
char deststr[64], gwstr[64];
char *actionstr = "New";
if (action == RTM_DELROUTE)
actionstr = "Delete";
+
+ sockaddr_to_string(&route->dest, deststr, sizeof(deststr));
+ sockaddr_to_string(&route->gw_addr, gwstr, sizeof(gwstr));
+ log_debug("%s route to %s via %s dev %s table %i", actionstr,
+ deststr, gwstr, iface->name, iface->route_table);
+}
+
+static int is_default_gw(struct pingu_gateway *route)
+{
+ switch (route->dest.sa.sa_family) {
+ case AF_INET:
+ return ((route->dest.sin.sin_addr.s_addr == 0)
+ && (route->gw_addr.sin.sin_addr.s_addr != 0));
+ break;
+ case AF_INET6:
+ log_debug("TODO: ipv6");
+ break;
+ }
+ return FALSE;
+}
+
+static void netlink_route_cb_action(struct nlmsghdr *msg, int action)
+{
+ struct pingu_iface *iface;
+ struct rtmsg *rtm = NLMSG_DATA(msg);
+ struct rtattr *rta[RTA_MAX+1];
+
+ struct pingu_gateway route;
/* ignore route changes that we made ourselves via talk_fd */
if (msg->nlmsg_pid == getpid())
return;
-
+
netlink_parse_rtattr(rta, RTA_MAX, RTM_RTA(rtm), RTM_PAYLOAD(msg));
if (rta[RTA_OIF] == NULL || rta[RTA_GATEWAY] == NULL
|| rtm->rtm_family != PF_INET || rtm->rtm_table != RT_TABLE_MAIN)
return;
- if (rta[RTA_DST] != NULL)
- destination = *(in_addr_t *)RTA_DATA(rta[RTA_DST]);
-
- if (rta[RTA_PRIORITY] != NULL)
- metric = *(uint32_t *)RTA_DATA(rta[RTA_PRIORITY]);
-
+ gw_from_rtmsg(&route, rtm, rta);
iface = pingu_iface_get_by_index(*(int*)RTA_DATA(rta[RTA_OIF]));
if (iface == NULL)
return;
- gateway = *(in_addr_t *)RTA_DATA(rta[RTA_GATEWAY]);
-
- inet_ntop(rtm->rtm_family, &destination, deststr, sizeof(deststr));
- inet_ntop(rtm->rtm_family, &gateway, gwstr, sizeof(gwstr));
-
- log_debug("%s route to %s via %s dev %s table %i", actionstr,
- deststr, gwstr, iface->name, iface->route_table);
-
- netlink_route_modify(&talk_fd, action, destination,
- rtm->rtm_dst_len, gateway, metric,
- iface->index, rtm->rtm_table);
+ log_route_change(&route, iface, action);
+ netlink_route_modify(&talk_fd, action, &route,
+ iface->index, iface->route_table);
- if (destination == 0 && gateway != 0)
- pingu_iface_gateway(iface, rtm->rtm_family, &gateway,
- metric, action);
+ if (is_default_gw(&route))
+ pingu_iface_gw_action(iface, &route, action);
}
static void netlink_route_new_cb(struct nlmsghdr *msg)