* Try using netlink to add/remove routes.
*/
static
-int _route_netlink(const char* op_fam, int operation, int family, unsigned metric)
+int _route_netlink(const char* op_fam, int operation, int family, unsigned metric, const void* prefix, uint8_t len)
{
struct {
struct nlmsghdr nlh;
struct rtattr rta;
unsigned val;
} metric;
+ struct {
+ struct rtattr rta;
+ unsigned char ipdata[16]; /* IPv6 MAX */
+ } prefix;
} nlreq;
int resp;
+ size_t txsz = sizeof(nlreq) - sizeof(nlreq.prefix);
+ char in6addr[INET6_ADDRSTRLEN];
memset(&nlreq, 0, sizeof(nlreq));
- nlreq.nlh.nlmsg_len = sizeof(nlreq);
nlreq.nlh.nlmsg_type = operation;
nlreq.nlh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE;
if (operation == RTM_NEWROUTE)
nlreq.metric.rta.rta_type = RTA_PRIORITY;
nlreq.metric.val = metric;
- resp = rtnetlink_msg(op_fam, NULL, &nlreq, sizeof(nlreq), NULL, NULL, 0);
+ if (prefix) {
+ char nbytes;
+ switch (family) {
+ case AF_INET: nbytes = 4; break;
+ case AF_INET6: nbytes = 16; break;
+ default: error("Unable to add route given that address family isn't known and prefix was specified."); return 0;
+ }
+ nlreq.rtmsg.rtm_dst_len = len;
+ nlreq.prefix.rta.rta_type = RTA_DST;
+ nlreq.prefix.rta.rta_len = sizeof(nlreq.prefix.rta) + nbytes;
+ memcpy(nlreq.prefix.ipdata, prefix, nbytes);
+
+ txsz += nlreq.prefix.rta.rta_len;
+ }
+
+ nlreq.nlh.nlmsg_len = txsz;
+ resp = rtnetlink_msg(op_fam, NULL, &nlreq, txsz, NULL, NULL, 0);
+
/* In some cases the interface could be down already from kernel perspective,
* and routes already removed resulting in errno=ESRCH, treat as success */
if (resp == 0 || operation == RTM_DELROUTE && -resp == ESRCH)
return 1; /* success */
- error("Unable to %s %s default route: %s", operation == RTM_NEWROUTE ? "add" : "remove",
+ error("Unable to %s %s %s route: %s", operation == RTM_NEWROUTE ? "add" : "remove",
family == AF_INET ? "IPv4" : "IPv6",
+ prefix ? inet_ntop(family, prefix, in6addr, sizeof(in6addr)) : "default",
resp < 0 ? strerror(-resp) : "Netlink error");
return 0;
}
-#define route_netlink(operation, family, metric) _route_netlink(#operation "/" #family, operation, family, metric)
+#define route_netlink(operation, family, metric, prefix, length) _route_netlink(#operation "/" #family, operation, family, metric, prefix, length)
+
+/********************************************************************
+ * sifaddroute - add a non-default route to the system through the ppp interface.
+ * It's the caller responsiblity to ensure that prefix points to a buffer of appriate size:
+ * AF_INET => 4 bytes
+ * AF_INET6 => 16 bytes
+ */
+int sifaddroute(int family, const void* prefix, uint8_t len, unsigned metric)
+{
+ return route_netlink(RTM_NEWROUTE, family, metric, prefix, len);
+}
+
+/********************************************************************
+ * sifdelroute - remove a non-default route to the system through the ppp interface.
+ * It's the caller responsiblity to ensure that prefix points to a buffer of appriate size:
+ * AF_INET => 4 bytes
+ * AF_INET6 => 16 bytes
+ */
+int sifdelroute(int family, const void* prefix, uint8_t len, unsigned metric)
+{
+ return route_netlink(RTM_DELROUTE, family, metric, prefix, len);
+}
/********************************************************************
*
int sifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway)
{
/* try appending using netlink first */
- if (route_netlink(RTM_NEWROUTE, AF_INET, dfl_route_metric))
+ if (route_netlink(RTM_NEWROUTE, AF_INET, dfl_route_metric, NULL, 0))
return 1;
/* ok, that failed, let's see if we can use ioctl */
int cifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway)
{
/* try removing using netlink first */
- if (route_netlink(RTM_DELROUTE, AF_INET, dfl_route_metric))
+ if (route_netlink(RTM_DELROUTE, AF_INET, dfl_route_metric, NULL, 0))
return 1;
/* ok, that failed, let's see if we can use ioctl */
int sif6defaultroute (int unit, eui64_t ouraddr, eui64_t gateway)
{
/* try appending using netlink first */
- if (route_netlink(RTM_NEWROUTE, AF_INET6, dfl_route6_metric))
+ if (route_netlink(RTM_NEWROUTE, AF_INET6, dfl_route6_metric, NULL, 0))
return 1;
/* ok, that failed, let's see if we can use ioctl */
int cif6defaultroute (int unit, eui64_t ouraddr, eui64_t gateway)
{
/* try removing using netlink first */
- if (route_netlink(RTM_DELROUTE, AF_INET6, dfl_route6_metric))
+ if (route_netlink(RTM_DELROUTE, AF_INET6, dfl_route6_metric, NULL, 0))
return 1;
/* ok, that failed, let's see if we can use ioctl */