From 3e05cdd09405cc6133ebf38ea9ee9afc50fb280f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 4 Aug 2003 22:45:25 +0000 Subject: Initial checkin git-svn-id: file:///home/lennart/svn/public/ifmetric/trunk@3 b1ab5a0b-19c4-0310-9d9a-ef184d715f50 --- src/Makefile.am | 23 +++++++ src/getifn.c | 77 +++++++++++++++++++++ src/getifn.h | 7 ++ src/ifmetric.c | 204 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/nlrequest.c | 139 ++++++++++++++++++++++++++++++++++++++ src/nlrequest.h | 20 ++++++ 6 files changed, 470 insertions(+) create mode 100644 src/Makefile.am create mode 100644 src/getifn.c create mode 100644 src/getifn.h create mode 100644 src/ifmetric.c create mode 100644 src/nlrequest.c create mode 100644 src/nlrequest.h (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..ff501ea --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,23 @@ +# $Id$ + +# This file is part of ifmetric. +# +# ifmetric is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# ifmetric 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 ifmetric; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + +sbin_PROGRAMS = ifmetric + +ifmetric_SOURCES = ifmetric.c getifn.h getifn.c nlrequest.h nlrequest.c + diff --git a/src/getifn.c b/src/getifn.c new file mode 100644 index 0000000..74dfc72 --- /dev/null +++ b/src/getifn.c @@ -0,0 +1,77 @@ +#include +#include + +#include "getifn.h" +#include "nlrequest.h" + +struct getifn_info { + char ifname[IFNAMSIZ+1]; + int idx; +}; + +int getifn_callback(struct nlmsghdr *n, void *u) { + struct rtattr *a = NULL; + int l; + struct getifn_info *gi = (struct getifn_info*) u; + struct ifinfomsg *i; + + if (n->nlmsg_type != RTM_NEWLINK) { + fprintf(stderr, "NETLINK: Got response for wrong request.\n"); + return -1; + } + + i = NLMSG_DATA(n); + l = NLMSG_PAYLOAD(n, sizeof(struct ifinfomsg)); + a = IFLA_RTA(i); + + while(RTA_OK(a, l)) { + + switch(a->rta_type) { + case IFLA_IFNAME: + if (strncmp(RTA_DATA(a), gi->ifname, IFNAMSIZ) == 0) { + gi->idx = i->ifi_index; + return 0; + } + } + a = RTA_NEXT(a, l); + } + + return 0; +} + +int getifn(int s, char *iface) { + struct getifn_info gi; + + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char a[1024]; + } req; + + memset(&req, 0, sizeof(req)); + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_MATCH; + req.n.nlmsg_type = RTM_GETLINK; + + req.i.ifi_family = AF_UNSPEC; + req.i.ifi_change = -1; + + memset(&gi, 0, sizeof(gi)); + strncpy(gi.ifname, iface, IFNAMSIZ); + gi.idx = -1; + + if (addattr_l(&req.n, sizeof(req), IFLA_IFNAME, gi.ifname, IFNAMSIZ+1) < 0) { + fprintf(stderr, "Failed to add attribute to netlink message\n"); + return -1; + } + + if (netlink_request(s, (struct nlmsghdr*) &req, getifn_callback, &gi) < 0) + return -1; + + if (gi.idx < 0) { + fprintf(stderr, "Interface '%s' not existent.\n", iface); + return -1; + } + + return gi.idx; +} diff --git a/src/getifn.h b/src/getifn.h new file mode 100644 index 0000000..3c4cff8 --- /dev/null +++ b/src/getifn.h @@ -0,0 +1,7 @@ +#ifndef foogetifnhfoo +#define foogetifnhfoo + +/* Return the kernel index for the given interface. */ +int getifn(int s, char *iface); + +#endif diff --git a/src/ifmetric.c b/src/ifmetric.c new file mode 100644 index 0000000..a232061 --- /dev/null +++ b/src/ifmetric.c @@ -0,0 +1,204 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nlrequest.h" +#include "getifn.h" + +#define MAX_ROUTES 64 + +struct nlmsghdr* routes[MAX_ROUTES]; +int n_routes = 0; + +int enumerate_callback(struct nlmsghdr *n, void *u) { + struct rtmsg *r; + struct rtattr *a = NULL; + int l, *ifn = u; + + if (n->nlmsg_type != RTM_NEWROUTE) { + fprintf(stderr, "NETLINK: Got response for wrong request.\n"); + return -1; + } + + r = NLMSG_DATA(n); + l = NLMSG_PAYLOAD(n, sizeof(struct rtmsg)); + a = RTM_RTA(r); + + if (r->rtm_table != RT_TABLE_MAIN) + return 0; + + while(RTA_OK(a, l)) { + switch(a->rta_type) { + case RTA_OIF: + + if (RTA_PAYLOAD(a) != sizeof(int)) { + fprintf(stderr, "NETLINK: Recieved corrupt RTA_OIF payload.\n"); + return -1; + } + + if (*((int*) RTA_DATA(a)) == *ifn) { + + if (n_routes < MAX_ROUTES) { + struct nlmsghdr* copy; + + if (!(copy = malloc(n->nlmsg_len))) { + fprintf(stderr, "Could not allocate memory.\n"); + return -1; + } + + memcpy(copy, n, n->nlmsg_len); + routes[n_routes++] = copy; + + } else + fprintf(stderr, "Found too many routes.\n"); + + break; + } + + + } + + a = RTA_NEXT(a, l); + } + + return 0; +} + +int enumerate(int s, int ifn) { + struct { + struct nlmsghdr n; + struct rtmsg r; + char a[1024]; + } req; + + memset(&req, 0, sizeof(req)); + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_MATCH; + req.n.nlmsg_type = RTM_GETROUTE; + + req.r.rtm_family = AF_INET; + req.r.rtm_table = RT_TABLE_MAIN; + + return netlink_request(s, (struct nlmsghdr*) &req, enumerate_callback, &ifn); +} + +struct nlmsghdr* set_route_metric(struct nlmsghdr* n, int metric) { + struct rtmsg *r; + struct rtattr *a = NULL; + int l, t; + + r = NLMSG_DATA(n); + l = NLMSG_PAYLOAD(n, sizeof(struct rtmsg)); + a = RTM_RTA(r); + + while(RTA_OK(a, l)) { + switch(a->rta_type) { + case RTA_PRIORITY: + + if (RTA_PAYLOAD(a) != sizeof(int)) { + fprintf(stderr, "NETLINK: Recieved corrupt RTA_PRIORITY payload.\n"); + return NULL; + } + + *((int*) RTA_DATA(a)) = metric; + return n; + } + + a = RTA_NEXT(a, l); + } + + if ((n = realloc(n, (t = n->nlmsg_len+1024)))) + addattr32(n, t, RTA_PRIORITY, metric); + else + fprintf(stderr, "realloc() failed.\n"); + + return n; +} + +int delete_route(int s, struct nlmsghdr* n) { + assert(s >= 0 && n); + + n->nlmsg_type = RTM_DELROUTE; + n->nlmsg_flags = NLM_F_REQUEST; + + return netlink_request(s, n, NULL, NULL); +} + +int add_route(int s, struct nlmsghdr* n) { + assert(s >= 0 && n); + + n->nlmsg_type = RTM_NEWROUTE; + n->nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE; + + return netlink_request(s, n, NULL, NULL); +} + + +int go(char *iface, int metric) { + int r = -1, j; + int s = -1, ifn; + + if ((s = netlink_open()) < 0) + return -1; + + if ((ifn = getifn(s, iface)) < 0) + goto finish; + + n_routes = 0; + if (enumerate(s, ifn) < 0) + goto finish; + + if (n_routes) { + for (j = 0; j < n_routes; j++) { + if (delete_route(s, routes[j]) >= 0) + if ((routes[j] = set_route_metric(routes[j], metric))) + add_route(s, routes[j]); + + free(routes[j]); + } + } + + r = 0; + +finish: + + if (s >= 0) + close(s); + + return r; +} + +int main(int argc, char *argv[]) { + char *iface; + int metric; + + if (argc <= 1 || (argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")))) { + char *b; + + if ((b = strrchr(argv[0], '/'))) + b++; + else + b = argv[0]; + + printf("Usage: %s [metric]\n" + "\n" + "%s is a tool for setting the metrics of all IPv4 routes\n" + "attached to a given network interface at once.\n" + "\n" + " The interface\n" + " The new metric (default: 0)\n", b, b); + return 0; + } + + iface = argv[1]; + metric = argc > 2 ? atoi(argv[2]) : 0; + + return go(iface, metric) < 0 ? 1 : 0; + +} diff --git a/src/nlrequest.c b/src/nlrequest.c new file mode 100644 index 0000000..1f72357 --- /dev/null +++ b/src/nlrequest.c @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include + +#include "nlrequest.h" + +int netlink_open(void) { + struct sockaddr_nl addr; + int s; + + if ((s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) { + fprintf(stderr, "socket(PF_NETLINK): %s\n", strerror(errno)); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_groups = 0; + addr.nl_pid = getpid(); + + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + fprintf(stderr, "bind(): %s\n", strerror(errno)); + return -1; + } + + return s; +} + + +int netlink_request(int s, struct nlmsghdr *n, int (*callback) (struct nlmsghdr *n, void *u), void *u) { + static int seq = 0; + pid_t pid = getpid(); + assert(s >= 0 && n); + + n->nlmsg_seq = seq++; + n->nlmsg_flags |= NLM_F_ACK; + + if (send(s, n, n->nlmsg_len, 0) < 0) { + fprintf(stderr, "NETLINK: send(): %s\n", strerror(errno)); + return -1; + } + + for (;;) { + int bytes; + char replybuf[1024]; + struct nlmsghdr *p = (struct nlmsghdr *) replybuf; + + if ((bytes = recv(s, &replybuf, sizeof(replybuf), 0)) < 0) { + fprintf(stderr, "recv(): %s\n", strerror(errno)); + return -1; + } + + for (; bytes > 0; p = NLMSG_NEXT(p, bytes)) { + int ret; + + if (!NLMSG_OK(p, bytes) || bytes < sizeof(struct nlmsghdr) || bytes < p->nlmsg_len) { + fprintf(stderr, "NETLINK: Packet too small or truncated!\n"); + return -1; + } + + if (p->nlmsg_type == NLMSG_DONE) + return 0; + + if (p->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *e = (struct nlmsgerr *) NLMSG_DATA (p); + + if (e->error) { + fprintf(stderr, "NETLINK: Error: %s\n", strerror(-e->error)); + return -1; + } else + return 0; + } + + if (p->nlmsg_pid != pid) + continue; + + if (p->nlmsg_seq != n->nlmsg_seq) { + fprintf(stderr, "NETLINK: Received message with bogus sequence number!\n"); + continue; + } + + if (callback) + if ((ret = callback(p, u)) < 0) + return ret; + } + } + + return 0; +} + +/* + * Utility function comes from iproute2. + * Author: Alexey Kuznetsov, + */ + +int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen) { + int len; + struct rtattr *rta; + + len = RTA_LENGTH(alen); + + if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) + return -1; + + rta = (struct rtattr*) (((char*)n) + NLMSG_ALIGN (n->nlmsg_len)); + rta->rta_type = type; + rta->rta_len = len; + memcpy (RTA_DATA(rta), data, alen); + n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + len; + + return 0; +} + +/* + * Utility function comes from iproute2. + * Author: Alexey Kuznetsov, + */ + + +int addattr32(struct nlmsghdr *n, int maxlen, int type, int data) { + int len; + struct rtattr *rta; + + len = RTA_LENGTH(4); + + if (NLMSG_ALIGN (n->nlmsg_len) + len > maxlen) + return -1; + + rta = (struct rtattr*) (((char*)n) + NLMSG_ALIGN (n->nlmsg_len)); + rta->rta_type = type; + rta->rta_len = len; + memcpy (RTA_DATA(rta), &data, 4); + n->nlmsg_len = NLMSG_ALIGN (n->nlmsg_len) + len; + + return 0; +} + diff --git a/src/nlrequest.h b/src/nlrequest.h new file mode 100644 index 0000000..efe3450 --- /dev/null +++ b/src/nlrequest.h @@ -0,0 +1,20 @@ +#ifndef foonlrequesthfoo +#define foonlrequesthfoo + +#include +#include +#include +#include +#include + +/* Issue a netlink message and wait for a response, calling 'callback' for every response message */ +int netlink_request(int s, struct nlmsghdr *n, int (*callback) (struct nlmsghdr *n, void*u), void *u); + +int addattr32(struct nlmsghdr *n, int maxlen, int type, int data); +int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen); + +int netlink_open(void); + + +#endif + -- cgit