I'm trying to write a small C based user space app that provides feature of managing routing rules using RTNETLINK. Below is an example accepting 3 arguments: add/del (rule), IP address and iface.
The problem with code below that it adds routing rule for "to" direction, while it doesn't for "from" direction. So basically code below is equal to: ip rule add to <src_addr> table <table_id>
, and I would like to rewrite it so it can also do ip rule add from <src_addr> table <table_id>
. Any suggestions?
/*
*
*/
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <linux/rtnetlink.h>
/* Open netlink socket */
int open_netlink()
{
struct sockaddr_nl saddr;
int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (sock < 0) {
perror("Failed to open netlink socket");
return -1;
}
memset(&saddr, 0, sizeof(saddr));
return sock;
}
/* Helper structure for ip address data and attributes */
typedef struct {
char family;
char bitlen;
unsigned char data[sizeof(struct in6_addr)];
} _inet_addr;
/* */
#define NLMSG_TAIL(nmsg) \
((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
/* Add new data to rtattr */
int rtattr_add(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen)
{
int len = RTA_LENGTH(alen);
struct rtattr *rta;
if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
fprintf(stderr, "rtattr_add error: message exceeded bound of %d\n", maxlen);
return -1;
}
rta = NLMSG_TAIL(n);
rta->rta_type = type;
rta->rta_len = len;
if (alen) {
memcpy(RTA_DATA(rta), data, alen);
}
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
return 0;
}
int do_rule(int sock, int cmd, int flags, _inet_addr *address, int if_idx)
{
struct {
struct nlmsghdr n;
struct rtmsg r;
char buf[4096];
} nl_request;
/* Initialize request structure */
nl_request.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
nl_request.n.nlmsg_flags = NLM_F_REQUEST | flags;
nl_request.n.nlmsg_type = cmd;
nl_request.r.rtm_family = address->family;
nl_request.r.rtm_table = 1;
nl_request.r.rtm_scope = RT_SCOPE_LINK;
/* Set additional flags if NOT deleting route */
if (cmd != RTM_DELRULE) {
nl_request.r.rtm_protocol = RTPROT_BOOT;
nl_request.r.rtm_type = RTN_UNICAST;
}
nl_request.r.rtm_family = address->family;
nl_request.r.rtm_dst_len = address->bitlen;
/* Select scope, for simplicity we supports here only IPv6 and IPv4 */
if (nl_request.r.rtm_family == AF_INET6) {
nl_request.r.rtm_scope = RT_SCOPE_UNIVERSE;
} else {
nl_request.r.rtm_scope = RT_SCOPE_LINK;
}
/* Set destination network */
rtattr_add(&nl_request.n, sizeof(nl_request), /*RTA_NEWDST*/ RTA_DST, &address->data, address->bitlen / 8);
/* Send message to the netlink */
return send(sock, &nl_request, sizeof(nl_request), 0);
}
/* Simple parser of the string IP address
*/
int read_addr(char *addr, _inet_addr *res)
{
if (strchr(addr, ':')) {
res->family = AF_INET6;
res->bitlen = 128;
} else {
res->family = AF_INET;
res->bitlen = 32;
}
return inet_pton(res->family, addr, res->data);
}
#define NEXT_CMD_ARG() do { argv++; if (--argc <= 0) exit(-1); } while(0)
int main(int argc, char **argv)
{
int default_gw = 0;
int if_idx = 0;
int nl_sock;
_inet_addr to_addr = { 0 };
_inet_addr gw_addr = { 0 };
_inet_addr address = { 0 };
int nl_cmd;
int nl_flags;
/* Parse command line arguments */
while (argc > 0) {
if (strcmp(*argv, "add") == 0) {
nl_cmd = RTM_NEWRULE;
nl_flags = NLM_F_CREATE | NLM_F_EXCL;
} else if (strcmp(*argv, "del") == 0) {
nl_cmd = RTM_DELRULE;
nl_flags = 0;
} else if (strcmp(*argv, "to") == 0) {
NEXT_CMD_ARG(); /* skip "to" and jump to the actual destination addr */
if (read_addr(*argv, &address) != 1) {
fprintf(stderr, "Failed to parse destination network %s\n", *argv);
exit(-1);
}
} else if (strcmp(*argv, "dev") == 0) {
NEXT_CMD_ARG(); /* skip "dev" */
if_idx = if_nametoindex(*argv);
}
argc--; argv++;
}
nl_sock = open_netlink();
if (nl_sock < 0) {
exit(-1);
}
// do_route(nl_sock, nl_cmd, nl_flags, &to_addr, &gw_addr, default_gw, if_idx);
do_rule(nl_sock, nl_cmd, nl_flags, &address, if_idx);
close (nl_sock);
return 0;
}
Created netlink socket and request, however parts of request structure might be configured incorrectly to achieve the goal.