netsukuku/src/if.c

624 lines
14 KiB
C
Raw Normal View History

2013-09-16 09:53:25 +00:00
/* This file is part of Netsukuku
* (c) Copyright 2005 Andrea Lo Pumo aka AlpT <alpt@freaknet.org>
*
* This source code 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.
*
* This source code 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.
* Please refer to the GNU Public License for more details.
*
* You should have received a copy of the GNU Public License along with
* this source code; if not, write to:
* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "includes.h"
#include <fnmatch.h>
#include "common.h"
#include "inet.h"
#include "if.h"
#include "libnetlink.h"
#include "ll_map.h"
extern int errno;
static struct
{
int ifindex;
int family;
int oneline;
int showqueue;
inet_prefix pfx;
int scope, scopemask;
int flags, flagmask;
int up;
char *label;
int flushed;
char *flushb;
int flushp;
int flushe;
struct rtnl_handle *rth;
} filter;
/*
* ifs_find_idx: returns the pointer to the interface struct of the
* device which has the index equal to `dev_idx'.
* `ifs' is the array which keeps the interface list and has `ifs_n' elements.
*/
interface *ifs_find_idx(interface *ifs, int ifs_n, int dev_idx)
{
int i;
for(i=0; i<ifs_n; i++)
if(ifs[i].dev_idx == dev_idx)
return &ifs[i];
return 0;
}
int ifs_find_devname(interface *ifs, int ifs_n, char *dev_name)
{
int i;
if(!dev_name)
return -1;
for(i=0; i<ifs_n; i++)
if(ifs[i].dev_name &&
!strncmp(ifs[i].dev_name, dev_name, IFNAMSIZ))
return i;
return -1;
}
/*
* ifs_del: removes from the `ifs' array the device which is at the
* `if_pos'th position. `*ifs_n' is then decremented.
*/
void ifs_del(interface *ifs, int *ifs_n, int if_pos)
{
if(if_pos == (*ifs_n)-1)
setzero(&ifs[if_pos], sizeof(interface));
else {
memcpy(&ifs[if_pos], &ifs[(*ifs_n)-1], sizeof(interface));
setzero(&ifs[(*ifs_n)-1], sizeof(interface));
}
(*ifs_n)--;
}
/*
* ifs_del_byname: deletes from the `ifs' array the device whose name is equal
* to `dev_name'
*/
void ifs_del_byname(interface *ifs, int *ifs_n, char *dev_name)
{
int if_pos;
if_pos=ifs_find_devname(ifs, *ifs_n, dev_name);
if(if_pos < 0)
return;
ifs_del(ifs, ifs_n, if_pos);
}
/*
* ifs_del_all_name: deleted from the `ifs' array all the device which have a
* device name that begins with `dev_name'. For example,
* ifs_del_all_name(ifs, ifs_n, "tun") deletes all the tunnel iifs
*/
void ifs_del_all_name(interface *ifs, int *ifs_n, char *dev_name)
{
int i, dev_len;
if(!dev_name || (dev_len=strlen(dev_name)) > IFNAMSIZ)
return;
for(i=0; i<(*ifs_n); i++) {
if(ifs[i].dev_name &&
!strncmp(ifs[i].dev_name, dev_name, dev_len)) {
ifs_del(ifs, ifs_n, i);
if(i <= (*ifs_n)-1)
i--;
}
}
}
/*
* ifs_get_pos: this is a stupid functions which returns the position of the
* struct in the `ifs' array which has the dev_idx element equal to
* `dev'->dev_idx. The `ifs' array has `ifs_n' members.
* If it is not found -1 is returned.
*/
int ifs_get_pos(interface *ifs, int ifs_n, interface *dev)
{
int i;
for(i=0; i<ifs_n; i++)
if(ifs[i].dev_idx == dev->dev_idx)
return i;
return -1;
}
/*
* get_dev: It returs the first dev it finds up and sets `*dev_ids' to the
* device's index. On error NULL is returned.
*/
const char *get_dev(int *dev_idx)
{
int idx;
if((idx=ll_first_up_if()) == -1) {
error("Couldn't find \"up\" devices. Set one dev \"up\", or "
"specify the device name in the options.");
return 0;
}
if(dev_idx)
*dev_idx=idx;
return ll_index_to_name(idx);
}
/*
* get_all_ifs: It fills the `ifs' array with all the network interfaces it
* finds up. The `ifs' array has `ifs_n'# members.
* It returns the number of filled interfaces.
*/
int get_all_up_ifs(interface *ifs, int ifs_n)
{
int i, idx, n;
for(i=0, n=0; i<ifs_n; i++) {
idx=ll_nth_up_if(n+1);
if(idx <= 0)
continue;
ifs[n].dev_idx=idx;
strncpy(ifs[n].dev_name, ll_index_to_name(idx), IFNAMSIZ);
loginfo("Network interface \"%s\" detected", ifs[n].dev_name);
n++;
if((idx-1) > i)
i=idx-1;
}
return n;
}
int set_flags(char *dev, u_int flags, u_int mask)
{
struct ifreq ifr;
int s;
strcpy(ifr.ifr_name, dev);
if((s=new_socket(AF_INET)) < 0) {
error("Error while setting \"%s\" flags: Cannot open socket", dev);
return -1;
}
if(ioctl(s, SIOCGIFFLAGS, &ifr)) {
error("Error while setting \"%s\" flags: %s", dev, strerror(errno));
close(s);
return -1;
}
ifr.ifr_flags &= ~mask;
ifr.ifr_flags |= mask&flags;
if(ioctl(s, SIOCSIFFLAGS, &ifr)) {
error("Error while setting \"%s\" flags: %s", dev, strerror(errno));
close(s);
return -1;
}
close(s);
return 0;
}
int set_dev_up(char *dev)
{
u_int mask=0, flags=0;
mask |= IFF_UP;
flags |= IFF_UP;
return set_flags(dev, flags, mask);
}
int set_dev_down(char *dev)
{
u_int mask=0, flags=0;
mask |= IFF_UP;
flags &= ~IFF_UP;
return set_flags(dev, flags, mask);
}
/*
* set_all_ifs: for all the `ifs_n' interfaces present in the `ifs' array, it
* calls the `set_func' functions, passing as argument ifs[i].dev_name.
* (All the above set_* functions can be used as `set_func').
* It returns the sum of all each return code, of set_func, therefore if it
* returns a negative value, some `set_func' gave an error.
*/
int set_all_ifs(interface *ifs, int ifs_n, int (*set_func)(char *dev))
{
int i, ret=0;
for(i=0; i<ifs_n; i++)
ret+=set_func(ifs[i].dev_name);
return ret;
}
/*
* if_init_all: it initializes all the `ifs_n'# interfaces present in the
* `ifs' array. If `ifs_n' is zero it gets all the current up interfaces and
* stores them in `new_ifs', updating the `new_ifs_n' counter too. Then it
* initializes them.
* In the `new_ifs' array, which must be at least big as the `ids' array, it
* stores all the initialized interfaces, updating the `new_ifs_n' counter.
* On error -1 is returned.
*/
int if_init_all(char *ifs_name[MAX_INTERFACES], int ifs_n,
interface *new_ifs, int *new_ifs_n)
{
struct rtnl_handle rth;
int ret=0, i, n;
if (rtnl_open(&rth, 0) < 0) {
error("Cannot open the rtnetlink socket to talk to the kernel's "
"soul");
return -1;
}
ll_init_map(&rth);
if(!ifs_n) {
ret=get_all_up_ifs(new_ifs, MAX_INTERFACES);
if(!ret)
return -1;
*new_ifs_n=ret;
} else {
for(i=0, n=0; i<ifs_n; i++) {
new_ifs[n].dev_idx=ll_name_to_index(ifs_name[n]);
if(!new_ifs[n].dev_idx) {
error("Cannot initialize the %s interface. "
"Ignoring it", ifs_name[n]);
continue;
}
strncpy(new_ifs[n].dev_name, ifs_name[n], IFNAMSIZ);
n++;
}
if(!n)
return -1;
*new_ifs_n=n;
}
if(set_all_ifs(new_ifs, *new_ifs_n, set_dev_up) < 0)
return -1;
rtnl_close(&rth);
return ret;
}
void if_close_all(void)
{
#if 0
/* XXX: disabled for now, it is buggy */
ll_free_index();
#endif
}
/*
* set_dev_ip: Assign the given `ip' to the interface named `dev'
* On success 0 is returned, -1 otherwise.
*/
int set_dev_ip(inet_prefix ip, char *dev)
{
int s=-1;
if(ip.family == AF_INET) {
struct ifreq req;
if((s=new_socket(AF_INET)) < 0) {
error("Error while setting \"%s\" ip: Cannot open socket", dev);
return -1;
}
strncpy(req.ifr_name, dev, IFNAMSIZ);
inet_to_sockaddr(&ip, 0, &req.ifr_addr, 0);
if(ioctl(s, SIOCSIFADDR, &req)) {
error("Error while setting \"%s\" ip: %s", dev, strerror(errno));
close(s);
return -1;
}
} else if(ip.family == AF_INET6) {
struct in6_ifreq req6;
struct sockaddr_in6 sin6;
struct sockaddr *sa=(struct sockaddr *)&sin6;
if((s=new_socket(AF_INET6)) < 0) {
error("Error while setting \"%s\" ip: Cannot open socket", dev);
return -1;
}
req6.ifr6_ifindex=ll_name_to_index(dev);
req6.ifr6_prefixlen=0;
inet_to_sockaddr(&ip, 0, sa, 0);
memcpy(&req6.ifr6_addr, sin6.sin6_addr.s6_addr32, ip.len);
if(ioctl(s, SIOCSIFADDR, &req6)) {
error("Error while setting \"%s\" ip: %s", dev, strerror(errno));
close(s);
return -1;
}
}
close(s);
return 0;
}
/*
* set_all_dev_ip: it sets the given `ip' to all the `ifs_n'# interfaces
* present in the `ifs' array.
* On error -1 is returned.
*/
int set_all_dev_ip(inet_prefix ip, interface *ifs, int ifs_n)
{
int i, ret=0;
for(i=0; i<ifs_n; i++)
ret+=set_dev_ip(ip, ifs[i].dev_name);
return ret;
}
/*
* get_dev_ip: fetches the ip currently assigned to the interface named `dev'
* and stores it to `ip'.
* On success 0 is returned, -1 otherwise.
*/
int get_dev_ip(inet_prefix *ip, int family, char *dev)
{
int s=-1;
int ret=0;
setzero(ip, sizeof(inet_prefix));
if((s=new_socket(family)) < 0) {
error("Error while setting \"%s\" ip: Cannot open socket", dev);
return -1;
}
if(family == AF_INET) {
struct ifreq req;
strncpy(req.ifr_name, dev, IFNAMSIZ);
req.ifr_addr.sa_family = family;
if(ioctl(s, SIOCGIFADDR, &req))
ERROR_FINISH(ret, -1, finish);
sockaddr_to_inet(&req.ifr_addr, ip, 0);
} else if(family == AF_INET6) {
struct in6_ifreq req6;
/*
* XXX: NOT TESTED
*/
req6.ifr6_ifindex=ll_name_to_index(dev);
req6.ifr6_prefixlen=0;
if(ioctl(s, SIOCGIFADDR, &req6))
ERROR_FINISH(ret, -1, finish);
inet_setip(ip, (u_int *)&req6.ifr6_addr, family);
}
finish:
if(s != -1)
close(s);
return ret;
}
/*
* All the code below this point is ripped from iproute2/iproute.c
* written by Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>.
*
* Modified lightly
*/
static int flush_update(void)
{
if (rtnl_send(filter.rth, filter.flushb, filter.flushp) < 0) {
error("Failed to send flush request: %s", strerror(errno));
return -1;
}
filter.flushp = 0;
return 0;
}
int print_addrinfo(const struct sockaddr_nl *who, struct nlmsghdr *n,
void *arg)
{
struct ifaddrmsg *ifa = NLMSG_DATA(n);
int len = n->nlmsg_len;
struct rtattr * rta_tb[IFA_MAX+1];
char b1[64];
if (n->nlmsg_type != RTM_NEWADDR && n->nlmsg_type != RTM_DELADDR)
return 0;
len -= NLMSG_LENGTH(sizeof(*ifa));
if (len < 0) {
error("BUG: wrong nlmsg len %d\n", len);
return -1;
}
if (filter.flushb && n->nlmsg_type != RTM_NEWADDR)
return 0;
parse_rtattr(rta_tb, IFA_MAX, IFA_RTA(ifa), n->nlmsg_len - NLMSG_LENGTH(sizeof(*ifa)));
if (!rta_tb[IFA_LOCAL])
rta_tb[IFA_LOCAL] = rta_tb[IFA_ADDRESS];
if (!rta_tb[IFA_ADDRESS])
rta_tb[IFA_ADDRESS] = rta_tb[IFA_LOCAL];
if (filter.ifindex && filter.ifindex != ifa->ifa_index)
return 0;
if ((filter.scope^ifa->ifa_scope)&filter.scopemask)
return 0;
if ((filter.flags^ifa->ifa_flags)&filter.flagmask)
return 0;
if (filter.label) {
const char *label;
if (rta_tb[IFA_LABEL])
label = RTA_DATA(rta_tb[IFA_LABEL]);
else
label = ll_idx_n2a(ifa->ifa_index, b1);
if (fnmatch(filter.label, label, 0) != 0)
return 0;
}
if (filter.pfx.family) {
if (rta_tb[IFA_LOCAL]) {
inet_prefix dst;
setzero(&dst, sizeof(dst));
dst.family = ifa->ifa_family;
memcpy(&dst.data, RTA_DATA(rta_tb[IFA_LOCAL]),
RTA_PAYLOAD(rta_tb[IFA_LOCAL]));
if (inet_addr_match(&dst, &filter.pfx, filter.pfx.bits))
return 0;
}
}
if (filter.flushb) {
struct nlmsghdr *fn;
if (NLMSG_ALIGN(filter.flushp) + n->nlmsg_len > filter.flushe) {
if (flush_update())
return -1;
}
fn = (struct nlmsghdr*)(filter.flushb + NLMSG_ALIGN(filter.flushp));
memcpy(fn, n, n->nlmsg_len);
fn->nlmsg_type = RTM_DELADDR;
fn->nlmsg_flags = NLM_F_REQUEST;
fn->nlmsg_seq = ++filter.rth->seq;
filter.flushp = (((char*)fn) + n->nlmsg_len) - filter.flushb;
filter.flushed++;
}
return 0;
}
struct nlmsg_list
{
struct nlmsg_list *next;
struct nlmsghdr h;
};
static int store_nlmsg(const struct sockaddr_nl *who, struct nlmsghdr *n,
void *arg)
{
struct nlmsg_list **linfo = (struct nlmsg_list**)arg;
struct nlmsg_list *h;
struct nlmsg_list **lp;
h = malloc(n->nlmsg_len+sizeof(void*));
if (h == NULL)
return -1;
memcpy(&h->h, n, n->nlmsg_len);
h->next = NULL;
for (lp = linfo; *lp; lp = &(*lp)->next) /* NOTHING */;
*lp = h;
ll_remember_index((struct sockaddr_nl *)who, n, NULL);
return 0;
}
int ip_addr_flush(int family, char *dev, int scope)
{
struct nlmsg_list *linfo = NULL;
struct rtnl_handle rth;
char *filter_dev = NULL;
setzero(&filter, sizeof(filter));
filter.showqueue = 1;
filter.family = family;
filter_dev = dev;
if (rtnl_open(&rth, 0) < 0)
return -1;
if (rtnl_wilddump_request(&rth, family, RTM_GETLINK) < 0) {
error("Cannot send dump request: %s", strerror(errno));
return -1;
}
if (rtnl_dump_filter(&rth, store_nlmsg, &linfo, NULL, NULL) < 0) {
error("Dump terminated");
return -1;
}
filter.ifindex = ll_name_to_index(filter_dev);
if (filter.ifindex <= 0) {
error("Device \"%s\" does not exist.", filter_dev);
return -1;
}
int round = 0;
char flushb[4096-512];
filter.flushb = flushb;
filter.flushp = 0;
filter.flushe = sizeof(flushb);
filter.rth = &rth;
filter.scopemask = -1;
filter.scope = scope;
for (;;) {
if (rtnl_wilddump_request(&rth, filter.family, RTM_GETADDR) < 0) {
error("Cannot send dump request: %s", strerror(errno));
return -1;
}
filter.flushed = 0;
if (rtnl_dump_filter(&rth, print_addrinfo, stdout, NULL, NULL) < 0) {
error("Flush terminated: %s", errno);
return -1;
}
if (filter.flushed == 0)
return 0;
round++;
if (flush_update() < 0)
return -1;
}
rtnl_close(&rth);
}
int ip_addr_flush_all_ifs(interface *ifs, int ifs_n, int family, int scope)
{
int i, ret=0;
for(i=0; i<ifs_n; i++)
ret+=ip_addr_flush(family, ifs[i].dev_name, scope);
return ret;
}