1. 實現前的准備工作
我們首先應該知道可以用 route, ip 命令來設置系統的靜態路由表。
#route # <== 查看路由表 Destination Gateway Genmask Flags Metric Ref Use Iface 10.0.0.0 * 255.255.255.0 U 1 0 0 eth0 link-local * 255.255.0.0 U 1000 0 0 eth0 default RTA1025W.home 0.0.0.0 UG 0 0 0 eth0
First entry tell you the following:
- Any traffic with destination 10.0.0.0 will not be using a gateway (thats the
*
on the line), will be using a 255.255.255.0 net mask, route is UP (that's the meaning of theU
) and which interface the route uses. IF you doifconfig -a
you will probably see that your eth0 IP address is within the 10.0.0.0 range, so this is you local network route.
Third entry tells you the following:
- If any traffic does not fit the traffic defined on any other rules then use this route. Imagine you want to visit www.yahoo.com, the address will be translated (I don't know what is the result address but its not in the 10.0.0.0 range for sure), because it wont fit on the 1st and the 2nd route it will be routed using the RTA1025W.home gateway, the metric will not mather and it will be using the interface defined on the default route line.
Q: Great, thanks! One more: if I send a datagram to a computer in my network, doesn't it have to go through the router? If so, isn't it using a gateway? Thanks
A: You router is your gateway in your route table, is your router not RTA1025W?
Q: Yes it is. So when two computers in my home network communicate, they have to go through the router right? I'm asking because for the first rule it is not defined as a gateway. Maybe its a gateway only when it is used to send information outside of the network?
A: Yeah, but the first rule tells your system that if the address translate from a hostname fits the first line (so all 10.0.0.0 addresses you can create with a netmask of 255.255.255.0), will not be routed to a gateway, that way your computers can communicate with each other without having to pass on the router (you can actually turn it off and your computers can contact each other).
1.1 查看一個命令的系統調用過程
root # strace route ...
rout # strace ip ...
通過上面的命令可以看到,route命令主要使用了 ioctl() 函數來設置路由,而ip命令主要使用了 ioctl(), recvmsg(), sendmsg()等函數。為什么不用ioctl()來統一設置呢?這是一個問題。
1.2 獲取源碼
root # route --version
net-tools 1.60
route命令來自 net-tools 1.60 源碼包:net-tools 1.60下載
root@fs000:~# ip -V
ip utility, iproute2-ss051107
ip命令來自 iproute2 源碼包:iproute2 下載
1.3 學習源碼
1. 數據結構
/* This structure gets passed by the SIOCADDRT and SIOCDELRT calls. */ struct rtentry /* Structure describing an Internet socket address. */ struct sockaddr_in /* ipv6 route struct */ struct in6_rtmsg rt; /* for IPv6. */ struct sockaddr_in6 /* interface request struct */ struct ifreq
2. ioctl() 函數
其主要功能與參數可以參考:Linux 網絡編程之ioctl函數
2. 代碼實現
/* * author: wangsd * description: setting the static route * copyright: GPL * history: * 2012.10.31 1.0 version ipv4/ipv6 route add/del */ #include <net/route.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <sys/ioctl.h> #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <unistd.h> #include <net/if.h> /* ifreq struct */ #include <netdb.h> #define RTACTION_ADD 1 /* add action */ #define RTACTION_DEL 2 /* del action */ void usage(); int inet_setroute(int action, char **args); int inet6_setroute(int action, char **args); /* main function */ int main(int argc, char **argv) { int action = 0; if(argc < 5) { usage(); return -1; } if(strcmp(argv[1], "-A")) { usage(); return -1; } if(!strcmp(argv[3], "add")) { action = RTACTION_ADD; } if(!strcmp(argv[3], "del")) { action = RTACTION_DEL; } /* add or del a ipv4 route item */ if(!strcmp(argv[2], "inet")) { inet_setroute(action, argv+4); } /* add or del a ipv6 route item */ if(!strcmp(argv[2], "inet6")) { inet6_setroute(action, argv+4); } return 0; } /* print usage information */ void usage() { printf("IPv4 Command: route -A inet add/del -net/-host TARGET netmask " "NETMASK gw GETWAY dev DEVICE mtu MTU\n"); printf("IPv6 Command: route -A inet6 add/del -net TARGET/PREFIX " "gw GETWAY dev DEVICE mtu MTU\n"); return ; } /* * IPv4 add/del route item in route table */ int inet_setroute(int action, char **args) { struct rtentry route; /* route item struct */ char target[128] = {0}; char gateway[128] = {0}; char netmask[128] = {0}; struct sockaddr_in *addr; int skfd; /* clear route struct by 0 */ memset((char *)&route, 0x00, sizeof(route)); /* default target is net (host)*/ route.rt_flags = RTF_UP ; args++; while(args) { if(*args == NULL) { break; } if(!strcmp(*args, "-net")) {/* default is a network target */ args++; strcpy(target, *args); addr = (struct sockaddr_in*) &route.rt_dst; addr->sin_family = AF_INET; addr->sin_addr.s_addr = inet_addr(target); args++; continue; } else if(!strcmp(*args, "-host")) {/* target is a host */ args++; strcpy(target, *args); addr = (struct sockaddr_in*) &route.rt_dst; addr->sin_family = AF_INET; addr->sin_addr.s_addr = inet_addr(target); route.rt_flags |= RTF_HOST; args++; continue; } else { usage(); return -1; } if(!strcmp(*args, "netmask")) {/* netmask setting */ args++; strcpy(netmask, *args); addr = (struct sockaddr_in*) &route.rt_genmask; addr->sin_family = AF_INET; addr->sin_addr.s_addr = inet_addr(netmask); args++; continue; } if(!strcmp(*args, "gw") || !strcmp(*args, "gateway")) {/* gateway setting */ args++; strcpy(gateway, *args); addr = (struct sockaddr_in*) &route.rt_gateway; addr->sin_family = AF_INET; addr->sin_addr.s_addr = inet_addr(gateway); route.rt_flags |= RTF_GATEWAY; args++; continue; } if(!strcmp(*args, "device") || !strcmp(*args, "dev")) {/* device setting */ args++; route.rt_dev = *args; args++; continue; } if(!strcmp(*args, "mtu")) {/* mtu setting */ args++; route.rt_flags |= RTF_MTU; route.rt_mtu = atoi(*args); args++; continue; } /* if you have other options, please put them in this place, like the options above. */ } /* create a socket */ skfd = socket(AF_INET, SOCK_DGRAM, 0); if(skfd < 0) { perror("socket"); return -1; } /* tell the kernel to accept this route */ if(action == RTACTION_DEL) {/* del a route item */ if(ioctl(skfd, SIOCDELRT, &route) < 0) { perror("SIOCDELRT"); close(skfd); return -1; } } else {/* add a route item */ if(ioctl(skfd, SIOCADDRT, &route) < 0) { perror("SIOCADDRT"); close(skfd); return -1; } } (void) close(skfd); return 0; } int INET6_resolve(char *name, struct sockaddr_in6 *sin6); int INET6_input(int type, char *bufp, struct sockaddr *sap); int INET6_getsock(char *bufp, struct sockaddr *sap); /* IPv6 add/del route item in route table */ /* main part of this function is from net-tools inet6_sr.c file */ int inet6_setroute(int action, char **args) { struct in6_rtmsg rt; /* ipv6 route struct */ struct ifreq ifr; /* interface request struct */ struct sockaddr_in6 sa6; /* ipv6 socket address */ char target[128]; char gateway[128] = "NONE"; int metric; int prefix_len; /* network prefix length */ char *devname = NULL; /* device name */ char *cp; int mtu = 0; int skfd = -1; if (*args == NULL ) { usage(); return -1; } args++; strcpy(target, *args); if (!strcmp(target, "default")) { prefix_len = 0; memset(&sa6, 0, sizeof(sa6)); } else { if ((cp = strchr(target, '/'))) { prefix_len = atol(cp + 1); if ((prefix_len < 0) || (prefix_len > 128)) usage(); *cp = 0; } else { prefix_len = 128; } if (INET6_input(1, target, (struct sockaddr *) &sa6) < 0 && INET6_input(0, target, (struct sockaddr *) &sa6) < 0) { return (1); } } /* Clean out the RTREQ structure. */ memset((char *) &rt, 0, sizeof(struct in6_rtmsg)); memcpy(&rt.rtmsg_dst, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr)); /* Fill in the other fields. */ rt.rtmsg_flags = RTF_UP; if (prefix_len == 128) rt.rtmsg_flags |= RTF_HOST; rt.rtmsg_metric = 1; rt.rtmsg_dst_len = prefix_len; args++; while (*args) { if (!strcmp(*args, "metric")) { args++; if (!*args || !isdigit(**args)) { usage(); return -1; } metric = atoi(*args); rt.rtmsg_metric = metric; args++; continue; } if (!strcmp(*args, "gw") || !strcmp(*args, "gateway")) { args++; if (!*args) return -1; if (rt.rtmsg_flags & RTF_GATEWAY) return -1; strcpy(gateway, *args); if (INET6_input(1, gateway, (struct sockaddr *) &sa6) < 0) { return -1; } memcpy(&rt.rtmsg_gateway, sa6.sin6_addr.s6_addr, sizeof(struct in6_addr)); rt.rtmsg_flags |= RTF_GATEWAY; args++; continue; } if (!strcmp(*args, "mod")) { args++; rt.rtmsg_flags |= RTF_MODIFIED; continue; } if (!strcmp(*args, "dyn")) { args++; rt.rtmsg_flags |= RTF_DYNAMIC; continue; } if (!strcmp(*args, "mtu")) { args++; mtu = atoi(*args); args++; continue; } if (!strcmp(*args, "device") || !strcmp(*args, "dev")) { args++; if (!*args) return -1; } else if (args[1]) return -1; devname = *args; args++; } /* Create a socket to the INET6 kernel. */ if ((skfd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) { perror("socket"); return -1; } memset(&ifr, 0, sizeof(ifr)); if (devname) {/* device setting */ strcpy(ifr.ifr_name, devname); if (ioctl(skfd, SIOGIFINDEX, &ifr) < 0) { perror("SIOGIFINDEX"); return -1; } rt.rtmsg_ifindex = ifr.ifr_ifindex; } if (mtu) {/* mtu setting */ ifr.ifr_mtu = mtu; if (ioctl(skfd, SIOCSIFMTU, &ifr) < 0) { perror("SIOCGIFMTU"); return -1; } } /* Tell the kernel to accept this route. */ if (action == RTACTION_DEL) { if (ioctl(skfd, SIOCDELRT, &rt) < 0) { perror("SIOCDELRT"); close(skfd); return -1; } } else { if (ioctl(skfd, SIOCADDRT, &rt) < 0) { perror("SIOCADDRT"); close(skfd); return -1; } } /* Close the socket. */ (void) close(skfd); return (0); } /* * following functions are ipv6 address transfrom * (from string to address struct and so on.) * these functions from net-tools inet6.c file. */ int INET6_resolve(char *name, struct sockaddr_in6 *sin6) { struct addrinfo req, *ai; int s; memset (&req, '\0', sizeof req); req.ai_family = AF_INET6; if ((s = getaddrinfo(name, NULL, &req, &ai))) { //perror("getaddrinfo"); fprintf(stderr, "getaddrinfo: %s: %d\n", name, s); return -1; } memcpy(sin6, ai->ai_addr, sizeof(struct sockaddr_in6)); freeaddrinfo(ai); return (0); } int INET6_getsock(char *bufp, struct sockaddr *sap) { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *) sap; sin6->sin6_family = AF_INET6; sin6->sin6_port = 0; if (inet_pton(AF_INET6, bufp, sin6->sin6_addr.s6_addr) <= 0) return (-1); return 16; /* ?;) */ } int INET6_input(int type, char *bufp, struct sockaddr *sap) { switch (type) { case 1: return (INET6_getsock(bufp, sap)); default: return (INET6_resolve(bufp, (struct sockaddr_in6 *) sap)); } } /*------------------------end-----------------------------------*/
命令投入的例子:
### IPv4
my_route -A inet add -net 192.56.76.0 netmask 255.255.255.0 dev eth0
my_route -A inet add -net 192.56.76.0 netmask 255.255.255.0 dev eth0
### IPv6
my_route -A inet6 add -net ::/0 dev eth0 mtu 1500
my_route -A inet6 del -net ::/0 dev eth0 mtu 1500