📄 dyn_ip.c
字号:
/* $Id: dyn_ip.c,v 1.79 2001/10/04 16:46:37 jm Exp $ * Tunnel and route interface to kernel * * Dynamic hierarchial IP tunnel * Copyright (C) 1998-2001, Dynamics group * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. See README and COPYING for * more details. *//* This interface code is heavily based on Alexey Kuznetsov's iproute2 */#define DEBUG_FLAG 'K'#include "config.h"#include "owntypes.h"#ifndef _GNU_SOURCE#define _GNU_SOURCE#endif#include <stdio.h>#include <stdlib.h>#include <string.h>#include <assert.h>#include <net/if.h>#include <netinet/in.h>#include <sys/socket.h>#include <arpa/inet.h>#include <sys/ioctl.h>#include <netinet/ip.h>#include <errno.h>#include <unistd.h>#ifdef DYN_TARGET_LINUX#include <linux/if_tunnel.h>#include <linux/netlink.h>#include <linux/rtnetlink.h>#include <net/if_arp.h>#endif#include <time.h>#include <fcntl.h>#include <sys/uio.h>#include "debug.h"#include "dyn_ip.h"#include "util.h"#ifdef DYN_TARGET_WINDOWS#include "windows_extra.h"#endif#ifndef MSG_TRUNC#define MSG_TRUNC 0x20#endif#ifndef IPPROTO_GRE#define IPPROTO_GRE 47#endif#ifdef DYN_TARGET_LINUXstruct rtnl_handle{ int fd; struct sockaddr_nl local; __u32 seq; __u32 dump;};static struct idxmap *idxmap = NULL;/* generic tunnel ioctl caller for dyn_ip_tunnel_add(), * dyn_ip_tunnel_add_gre(), and dyn_ip_tunnel_del() */static int tunnel_ioctl(int cmd, const char *dev, __u32 remote, __u32 local, int proto, __u32 key){ struct ifreq ifr; struct ip_tunnel_parm p; int s; assert(dev != NULL && strlen(dev) < IFNAMSIZ); memset(&p, 0, sizeof(p)); p.iph.version = 4; p.iph.ihl = 5; p.iph.frag_off = htons(IP_DF); /* Flag: Don't Fragment */ p.iph.ttl = 64; /* fixed TTL for the outer IP header to make the * tunnel transparent for traceroute like applications */ dynamics_strlcpy(p.name, dev, IFNAMSIZ); if (cmd == SIOCADDTUNNEL) { if (key != 0 && proto == IPPROTO_GRE) { p.i_key = p.o_key = htonl(key); p.i_flags |= htons(0x2000); /* GRE_KEY */ p.o_flags |= htons(0x2000); /* GRE_KEY */ } p.iph.protocol = proto; p.iph.daddr = remote; p.iph.saddr = local; if (proto == IPPROTO_IPIP) dynamics_strlcpy(ifr.ifr_name, "tunl0", IFNAMSIZ); else if (proto == IPPROTO_GRE) dynamics_strlcpy(ifr.ifr_name, "gre0", IFNAMSIZ); } else if (cmd == SIOCDELTUNNEL) { dynamics_strlcpy(ifr.ifr_name, dev, IFNAMSIZ); } else { DEBUG(DEBUG_FLAG, "tunnel_ioctl: Unknown cmd %i\n", cmd); return -1; } ifr.ifr_ifru.ifru_data = (void*) &p; s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { DEBUG(DEBUG_FLAG, "tunnel_ioctl: socket: %s\n", strerror(errno)); return -1; } if (ioctl(s, cmd, &ifr) != 0) { DEBUG(DEBUG_FLAG, "tunnel_ioctl: ioctl: %s\n", strerror(errno)); close(s); return -1; } close(s); return 0;}/** * dyn_ip_tunnel_add: * @dev: the device name of the tunnel to add * @remote: IP address of remote end of tunnel * @local: IP address of local end of tunnel * * Add an IP Encapsulation within IP (RFC 2003) tunnel named * @dev between @local and @remote. * * Returns: 0 if successful, else -1 */int dyn_ip_tunnel_add(const char *dev, struct in_addr remote, struct in_addr local){ return tunnel_ioctl(SIOCADDTUNNEL, dev, remote.s_addr, local.s_addr, IPPROTO_IPIP, 0);}/** * dyn_ip_tunnel_add_gre: * @dev: the device name of the tunnel to add * @remote: IP address of remote end of tunnel * @local: IP address of local end of tunnel * @key: Key field for the GRE header (used to index the tunnels between same * end-points) * * Add a Generic Routing Encapsulation (RFC 1701) tunnel named @dev * between @local and @remote. * * Returns: 0 if successful, else -1 */int dyn_ip_tunnel_add_gre(const char *dev, struct in_addr remote, struct in_addr local, int key){ return tunnel_ioctl(SIOCADDTUNNEL, dev, remote.s_addr, local.s_addr, IPPROTO_GRE, key);}/** * dyn_ip_tunnel_del: * @dev: the name of the device to delete * * Delete tunnel named @dev. * * Returns: 0 if successful, else -1 */int dyn_ip_tunnel_del(const char *dev){ return tunnel_ioctl(SIOCDELTUNNEL, dev, 0, 0, IPPROTO_IPIP, 0);}/** * dyn_ip_link_set_dev: * @dev: interface to be modified * @flag: flags to be set * @set: 1=set given flags, 0=unset given flags * * Modifies given interface @dev by setting or unsetting interface flags @flag. * * Returns: 0 on success or -1 on failure */static int dyn_ip_link_set_dev(const char *dev, int flag, int set){ struct ifreq ifr; int s, changed; assert(dev != NULL); assert(strlen(dev) < IFNAMSIZ); dynamics_strlcpy(ifr.ifr_name, dev, IFNAMSIZ); s = socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) { DEBUG(DEBUG_FLAG, "dyn_ip_link_set_dev: socket: %s\n", strerror(errno)); return -1; } if (ioctl(s, SIOCGIFFLAGS, &ifr) != 0) { DEBUG(DEBUG_FLAG, "dyn_ip_link_set_dev: ioctl SIOCGIFFLAGS: %s\n", strerror(errno)); close(s); return -1; } changed = 0; if (set) { if ((ifr.ifr_flags & flag) == 0) { ifr.ifr_flags |= flag; changed = 1; } } else { if ((ifr.ifr_flags & flag) != 0) { ifr.ifr_flags &= ~flag; changed = 1; } } if (changed) { if (ioctl(s, SIOCSIFFLAGS, &ifr) != 0) { DEBUG(DEBUG_FLAG, "dyn_ip_link_set_dev_up: ioctl SIOCSIFFLAGS: " "%s\n", strerror(errno)); close(s); return -1; } } close(s); return 0;}/** * dyn_ip_link_set_dev_up: * @dev: network device name * * Set the link on device @dev up. * * Returns: 0 if successful, else -1 */int dyn_ip_link_set_dev_up(const char *dev){ return dyn_ip_link_set_dev(dev, IFF_UP, 1);}/** * dyn_ip_link_set_dev_down: * @dev: network device name * * Set the link on device @dev down. * * Returns: 0 if successful, else -1 */int dyn_ip_link_set_dev_down(const char *dev){ return dyn_ip_link_set_dev(dev, IFF_UP, 0);}/** * rtnl_open: * @rth: * @subscriptions: * * * * Returns: */static int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions){ unsigned int len; assert(rth != NULL); rth->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (rth->fd < 0) { DEBUG(DEBUG_FLAG, "rtnl_open: cannot open netlink socket: %s\n", strerror(errno)); return -1; } memset(&rth->local, 0, sizeof(rth->local)); rth->local.nl_family = AF_NETLINK; rth->local.nl_groups = subscriptions; if (bind(rth->fd, (struct sockaddr *) &rth->local, sizeof(rth->local)) < 0) { DEBUG(DEBUG_FLAG, "rtnl_open: cannot bind netlink socket: %s\n", strerror(errno)); close(rth->fd); return -1; } len = sizeof(rth->local); if (getsockname(rth->fd, (struct sockaddr *) &rth->local, &len) < 0) { DEBUG(DEBUG_FLAG, "rtnl_open: cannot getsockname: %s\n", strerror(errno)); close(rth->fd); return -1; } if (len != sizeof(rth->local)) { DEBUG(DEBUG_FLAG, "rtnl_open: wrong address length %i != %i\n", len, sizeof(rth->local)); close(rth->fd); return -1; } if (rth->local.nl_family != AF_NETLINK) { DEBUG(DEBUG_FLAG, "rtnl_open: wrong address family %i != %i\n", rth->local.nl_family, AF_NETLINK); close(rth->fd); return -1; } rth->seq = time(NULL); return 0;}/** * rtnl_talk: * @rth: * @n: * @answer: * * * * Returns: */static int rtnl_talk(struct rtnl_handle *rth, struct nlmsghdr *n, struct nlmsghdr *answer){ int status; struct nlmsghdr *h; struct sockaddr_nl nladdr; struct iovec iov; char buf[8192]; struct msghdr msg = { (void *) &nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0 }; iov.iov_base = (void*) n; iov.iov_len = n->nlmsg_len; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; nladdr.nl_pid = 0; nladdr.nl_groups = 0; n->nlmsg_seq = ++rth->seq; if (answer == NULL) n->nlmsg_flags |= NLM_F_ACK; status = sendmsg(rth->fd, &msg, 0); if (status < 0) { DEBUG(DEBUG_FLAG, "rtnl_talk: sendmsg: %s\n", strerror(errno)); return -1; } iov.iov_base = buf; iov.iov_len = sizeof(buf); do { status = recvmsg(rth->fd, &msg, 0); if (status < 0) { if (errno == EINTR) continue; DEBUG(DEBUG_FLAG, "rtnl_talk: OVERRUN: %s\n", strerror(errno)); continue; } if (status == 0) { DEBUG(DEBUG_FLAG, "rtnl_talk: EOF on netlink\n"); return -1; } if (msg.msg_namelen != sizeof(nladdr)) { DEBUG(DEBUG_FLAG, "rtnl_talk: sender address length == %i != %i\n", msg.msg_namelen, sizeof(nladdr)); return -1; } for (h = (struct nlmsghdr*) buf; status >= sizeof(*h); ) { int len = h->nlmsg_len; pid_t pid = h->nlmsg_pid; int l = len - sizeof(*h); unsigned seq = h->nlmsg_seq; if (l < 0 || len > status) { if (msg.msg_flags & MSG_TRUNC) { DEBUG(DEBUG_FLAG, "rtnl_talk: " "truncated message\n"); return -1; } DEBUG(DEBUG_FLAG, "rtnl_talk: malformed message: len=%d\n", len); return -1; } if (h->nlmsg_pid != pid || h->nlmsg_seq != seq) { /* junk */ continue; } if (h->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA(h); if (l < sizeof(struct nlmsgerr)) { DEBUG(DEBUG_FLAG, "rtnl_talk: ERROR truncated\n"); return -1; } errno = -err->error; if (errno == 0) { if (answer) memcpy(answer, h, h->nlmsg_len); return 0; } DEBUG(DEBUG_FLAG, "rtnl_talk: RTNETLINK error: " "%s\n", strerror(errno)); return -1; } if (answer) { memcpy(answer, h, h->nlmsg_len); return 0; } DEBUG(DEBUG_FLAG, "rtnl_talk: unexpected reply\n"); status -= NLMSG_ALIGN(len); h = (struct nlmsghdr *) (((char *) h) + NLMSG_ALIGN(len)); } if (msg.msg_flags & MSG_TRUNC) { DEBUG(DEBUG_FLAG, "rtnl_talk: message truncated\n"); continue; } if (status) { DEBUG(DEBUG_FLAG, "rtnl_talk: remnant of size %i\n", status); return -1; } } while (1);}/** * addattr32: * @n: * @maxlen: * @type: * @data: * * * * Returns: */static int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 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;}/** * addattr_l: * @n: * @maxlen: * @type: * @data: * @alen: * * * * Returns: */static int addattr_l(struct nlmsghdr *n, int maxlen, int type, const 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;}static int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type){ struct { struct nlmsghdr nlh; struct rtgenmsg g; } req; struct sockaddr_nl nladdr; memset(&nladdr, 0, sizeof(nladdr)); nladdr.nl_family = AF_NETLINK; req.nlh.nlmsg_len = sizeof(req); req.nlh.nlmsg_type = type; req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; req.nlh.nlmsg_pid = 0; req.nlh.nlmsg_seq = rth->dump = ++rth->seq; req.g.rtgen_family = family; return sendto(rth->fd, (void *) &req, sizeof(req), 0, (struct sockaddr *) &nladdr, sizeof(nladdr));}static int rtnl_dump_filter(struct rtnl_handle *rth, int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *), void *arg1){ char buf[8192]; struct sockaddr_nl nladdr; struct iovec iov = { buf, sizeof(buf) }; do { int status; struct nlmsghdr *h;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -