summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2003-08-04 22:45:25 +0000
committerLennart Poettering <lennart@poettering.net>2003-08-04 22:45:25 +0000
commit3e05cdd09405cc6133ebf38ea9ee9afc50fb280f (patch)
tree22de63df1a8223e84fb1c028ca5ebf178e97cfc8 /src
parent80f8eef17230e586d343d0c219f9eb2f2d1643e8 (diff)
Initial checkin
git-svn-id: file:///home/lennart/svn/public/ifmetric/trunk@3 b1ab5a0b-19c4-0310-9d9a-ef184d715f50
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am23
-rw-r--r--src/getifn.c77
-rw-r--r--src/getifn.h7
-rw-r--r--src/ifmetric.c204
-rw-r--r--src/nlrequest.c139
-rw-r--r--src/nlrequest.h20
6 files changed, 470 insertions, 0 deletions
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 <string.h>
+#include <stdio.h>
+
+#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 <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <linux/types.h>
+#include <linux/if.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#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 <iface> [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"
+ " <iface> The interface\n"
+ " <metric> 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 <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#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, <kuznet@ms2.inr.ac.ru>
+ */
+
+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, <kuznet@ms2.inr.ac.ru>
+ */
+
+
+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 <sys/socket.h>
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/if.h>
+
+/* 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
+