dv.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:12k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  * INET An implementation of the TCP/IP protocol suite for the LINUX
  3.  * operating system.  INET is implemented using the  BSD Socket
  4.  * interface as the means of communication with the user level.
  5.  *
  6.  * Generic frame diversion
  7.  *
  8.  * Version: @(#)eth.c 0.41 09/09/2000
  9.  *
  10.  * Authors:
  11.  *  Benoit LOCHER: initial integration within the kernel with support for ethernet
  12.  *  Dave Miller: improvement on the code (correctness, performance and source files)
  13.  *
  14.  */
  15. #include <linux/types.h>
  16. #include <linux/kernel.h>
  17. #include <linux/sched.h>
  18. #include <linux/string.h>
  19. #include <linux/mm.h>
  20. #include <linux/socket.h>
  21. #include <linux/in.h>
  22. #include <linux/inet.h>
  23. #include <linux/ip.h>
  24. #include <linux/udp.h>
  25. #include <linux/netdevice.h>
  26. #include <linux/etherdevice.h>
  27. #include <linux/skbuff.h>
  28. #include <linux/errno.h>
  29. #include <linux/init.h>
  30. #include <net/dst.h>
  31. #include <net/arp.h>
  32. #include <net/sock.h>
  33. #include <net/ipv6.h>
  34. #include <net/ip.h>
  35. #include <asm/uaccess.h>
  36. #include <asm/system.h>
  37. #include <asm/checksum.h>
  38. #include <linux/divert.h>
  39. #include <linux/sockios.h>
  40. const char sysctl_divert_version[32]="0.46"; /* Current version */
  41. int __init dv_init(void)
  42. {
  43. printk(KERN_INFO "NET4: Frame Diverter %sn", sysctl_divert_version);
  44. return 0;
  45. }
  46. /*
  47.  * Allocate a divert_blk for a device. This must be an ethernet nic.
  48.  */
  49. int alloc_divert_blk(struct net_device *dev)
  50. {
  51. int alloc_size = (sizeof(struct divert_blk) + 3) & ~3;
  52. if (dev->type == ARPHRD_ETHER) {
  53. printk(KERN_DEBUG "divert: allocating divert_blk for %sn",
  54.        dev->name);
  55. dev->divert = (struct divert_blk *)
  56. kmalloc(alloc_size, GFP_KERNEL);
  57. if (dev->divert == NULL) {
  58. printk(KERN_DEBUG "divert: unable to allocate divert_blk for %sn",
  59.        dev->name);
  60. return -ENOMEM;
  61. } else {
  62. memset(dev->divert, 0, sizeof(struct divert_blk));
  63. }
  64. dev_hold(dev);
  65. } else {
  66. printk(KERN_DEBUG "divert: not allocating divert_blk for non-ethernet device %sn",
  67.        dev->name);
  68. dev->divert = NULL;
  69. }
  70. return 0;
  71. /*
  72.  * Free a divert_blk allocated by the above function, if it was 
  73.  * allocated on that device.
  74.  */
  75. void free_divert_blk(struct net_device *dev)
  76. {
  77. if (dev->divert) {
  78. kfree(dev->divert);
  79. dev->divert=NULL;
  80. dev_put(dev);
  81. printk(KERN_DEBUG "divert: freeing divert_blk for %sn",
  82.        dev->name);
  83. } else {
  84. printk(KERN_DEBUG "divert: no divert_blk to free, %s not ethernetn",
  85.        dev->name);
  86. }
  87. }
  88. /*
  89.  * Adds a tcp/udp (source or dest) port to an array
  90.  */
  91. int add_port(u16 ports[], u16 port)
  92. {
  93. int i;
  94. if (port == 0)
  95. return -EINVAL;
  96. /* Storing directly in network format for performance,
  97.  * thanks Dave :)
  98.  */
  99. port = htons(port);
  100. for (i = 0; i < MAX_DIVERT_PORTS; i++) {
  101. if (ports[i] == port)
  102. return -EALREADY;
  103. }
  104. for (i = 0; i < MAX_DIVERT_PORTS; i++) {
  105. if (ports[i] == 0) {
  106. ports[i] = port;
  107. return 0;
  108. }
  109. }
  110. return -ENOBUFS;
  111. }
  112. /*
  113.  * Removes a port from an array tcp/udp (source or dest)
  114.  */
  115. int remove_port(u16 ports[], u16 port)
  116. {
  117. int i;
  118. if (port == 0)
  119. return -EINVAL;
  120. /* Storing directly in network format for performance,
  121.  * thanks Dave !
  122.  */
  123. port = htons(port);
  124. for (i = 0; i < MAX_DIVERT_PORTS; i++) {
  125. if (ports[i] == port) {
  126. ports[i] = 0;
  127. return 0;
  128. }
  129. }
  130. return -EINVAL;
  131. }
  132. /* Some basic sanity checks on the arguments passed to divert_ioctl() */
  133. int check_args(struct divert_cf *div_cf, struct net_device **dev)
  134. {
  135. char devname[32];
  136. int ret;
  137. if (dev == NULL)
  138. return -EFAULT;
  139. /* GETVERSION: all other args are unused */
  140. if (div_cf->cmd == DIVCMD_GETVERSION)
  141. return 0;
  142. /* Network device index should reasonably be between 0 and 1000 :) */
  143. if (div_cf->dev_index < 0 || div_cf->dev_index > 1000) 
  144. return -EINVAL;
  145. /* Let's try to find the ifname */
  146. sprintf(devname, "eth%d", div_cf->dev_index);
  147. *dev = dev_get_by_name(devname);
  148. /* dev should NOT be null */
  149. if (*dev == NULL)
  150. return -EINVAL;
  151. ret = 0;
  152. /* user issuing the ioctl must be a super one :) */
  153. if (!capable(CAP_SYS_ADMIN)) {
  154. ret = -EPERM;
  155. goto out;
  156. }
  157. /* Device must have a divert_blk member NOT null */
  158. if ((*dev)->divert == NULL)
  159. ret = -EINVAL;
  160. out:
  161. dev_put(*dev);
  162. return ret;
  163. }
  164. /*
  165.  * control function of the diverter
  166.  */
  167. #define DVDBG(a)
  168. printk(KERN_DEBUG "divert_ioctl() line %d %sn", __LINE__, (a))
  169. int divert_ioctl(unsigned int cmd, struct divert_cf *arg)
  170. {
  171. struct divert_cf div_cf;
  172. struct divert_blk *div_blk;
  173. struct net_device *dev;
  174. int ret;
  175. switch (cmd) {
  176. case SIOCGIFDIVERT:
  177. DVDBG("SIOCGIFDIVERT, copy_from_user");
  178. if (copy_from_user(&div_cf, arg, sizeof(struct divert_cf)))
  179. return -EFAULT;
  180. DVDBG("before check_args");
  181. ret = check_args(&div_cf, &dev);
  182. if (ret)
  183. return ret;
  184. DVDBG("after checkargs");
  185. div_blk = dev->divert;
  186. DVDBG("befre switch()");
  187. switch (div_cf.cmd) {
  188. case DIVCMD_GETSTATUS:
  189. /* Now, just give the user the raw divert block
  190.  * for him to play with :)
  191.  */
  192. if (copy_to_user(div_cf.arg1.ptr, dev->divert,
  193.  sizeof(struct divert_blk)))
  194. return -EFAULT;
  195. break;
  196. case DIVCMD_GETVERSION:
  197. DVDBG("GETVERSION: checking ptr");
  198. if (div_cf.arg1.ptr == NULL)
  199. return -EINVAL;
  200. DVDBG("GETVERSION: copying data to userland");
  201. if (copy_to_user(div_cf.arg1.ptr,
  202.  sysctl_divert_version, 32))
  203. return -EFAULT;
  204. DVDBG("GETVERSION: data copied");
  205. break;
  206. default:
  207. return -EINVAL;
  208. };
  209. break;
  210. case SIOCSIFDIVERT:
  211. if (copy_from_user(&div_cf, arg, sizeof(struct divert_cf)))
  212. return -EFAULT;
  213. ret = check_args(&div_cf, &dev);
  214. if (ret)
  215. return ret;
  216. div_blk = dev->divert;
  217. switch(div_cf.cmd) {
  218. case DIVCMD_RESET:
  219. div_blk->divert = 0;
  220. div_blk->protos = DIVERT_PROTO_NONE;
  221. memset(div_blk->tcp_dst, 0,
  222.        MAX_DIVERT_PORTS * sizeof(u16));
  223. memset(div_blk->tcp_src, 0,
  224.        MAX_DIVERT_PORTS * sizeof(u16));
  225. memset(div_blk->udp_dst, 0,
  226.        MAX_DIVERT_PORTS * sizeof(u16));
  227. memset(div_blk->udp_src, 0,
  228.        MAX_DIVERT_PORTS * sizeof(u16));
  229. return 0;
  230. case DIVCMD_DIVERT:
  231. switch(div_cf.arg1.int32) {
  232. case DIVARG1_ENABLE:
  233. if (div_blk->divert)
  234. return -EALREADY;
  235. div_blk->divert = 1;
  236. break;
  237. case DIVARG1_DISABLE:
  238. if (!div_blk->divert)
  239. return -EALREADY;
  240. div_blk->divert = 0;
  241. break;
  242. default:
  243. return -EINVAL;
  244. };
  245. break;
  246. case DIVCMD_IP:
  247. switch(div_cf.arg1.int32) {
  248. case DIVARG1_ENABLE:
  249. if (div_blk->protos & DIVERT_PROTO_IP)
  250. return -EALREADY;
  251. div_blk->protos |= DIVERT_PROTO_IP;
  252. break;
  253. case DIVARG1_DISABLE:
  254. if (!(div_blk->protos & DIVERT_PROTO_IP))
  255. return -EALREADY;
  256. div_blk->protos &= ~DIVERT_PROTO_IP;
  257. break;
  258. default:
  259. return -EINVAL;
  260. };
  261. break;
  262. case DIVCMD_TCP:
  263. switch(div_cf.arg1.int32) {
  264. case DIVARG1_ENABLE:
  265. if (div_blk->protos & DIVERT_PROTO_TCP)
  266. return -EALREADY;
  267. div_blk->protos |= DIVERT_PROTO_TCP;
  268. break;
  269. case DIVARG1_DISABLE:
  270. if (!(div_blk->protos & DIVERT_PROTO_TCP))
  271. return -EALREADY;
  272. div_blk->protos &= ~DIVERT_PROTO_TCP;
  273. break;
  274. default:
  275. return -EINVAL;
  276. };
  277. break;
  278. case DIVCMD_TCPDST:
  279. switch(div_cf.arg1.int32) {
  280. case DIVARG1_ADD:
  281. return add_port(div_blk->tcp_dst,
  282. div_cf.arg2.uint16);
  283. case DIVARG1_REMOVE:
  284. return remove_port(div_blk->tcp_dst,
  285.    div_cf.arg2.uint16);
  286. default:
  287. return -EINVAL;
  288. };
  289. break;
  290. case DIVCMD_TCPSRC:
  291. switch(div_cf.arg1.int32) {
  292. case DIVARG1_ADD:
  293. return add_port(div_blk->tcp_src,
  294. div_cf.arg2.uint16);
  295. case DIVARG1_REMOVE:
  296. return remove_port(div_blk->tcp_src,
  297.    div_cf.arg2.uint16);
  298. default:
  299. return -EINVAL;
  300. };
  301. break;
  302. case DIVCMD_UDP:
  303. switch(div_cf.arg1.int32) {
  304. case DIVARG1_ENABLE:
  305. if (div_blk->protos & DIVERT_PROTO_UDP)
  306. return -EALREADY;
  307. div_blk->protos |= DIVERT_PROTO_UDP;
  308. break;
  309. case DIVARG1_DISABLE:
  310. if (!(div_blk->protos & DIVERT_PROTO_UDP))
  311. return -EALREADY;
  312. div_blk->protos &= ~DIVERT_PROTO_UDP;
  313. break;
  314. default:
  315. return -EINVAL;
  316. };
  317. break;
  318. case DIVCMD_UDPDST:
  319. switch(div_cf.arg1.int32) {
  320. case DIVARG1_ADD:
  321. return add_port(div_blk->udp_dst,
  322. div_cf.arg2.uint16);
  323. case DIVARG1_REMOVE:
  324. return remove_port(div_blk->udp_dst,
  325.    div_cf.arg2.uint16);
  326. default:
  327. return -EINVAL;
  328. };
  329. break;
  330. case DIVCMD_UDPSRC:
  331. switch(div_cf.arg1.int32) {
  332. case DIVARG1_ADD:
  333. return add_port(div_blk->udp_src,
  334. div_cf.arg2.uint16);
  335. case DIVARG1_REMOVE:
  336. return remove_port(div_blk->udp_src,
  337.    div_cf.arg2.uint16);
  338. default:
  339. return -EINVAL;
  340. };
  341. break;
  342. case DIVCMD_ICMP:
  343. switch(div_cf.arg1.int32) {
  344. case DIVARG1_ENABLE:
  345. if (div_blk->protos & DIVERT_PROTO_ICMP)
  346. return -EALREADY;
  347. div_blk->protos |= DIVERT_PROTO_ICMP;
  348. break;
  349. case DIVARG1_DISABLE:
  350. if (!(div_blk->protos & DIVERT_PROTO_ICMP))
  351. return -EALREADY;
  352. div_blk->protos &= ~DIVERT_PROTO_ICMP;
  353. break;
  354. default:
  355. return -EINVAL;
  356. };
  357. break;
  358. default:
  359. return -EINVAL;
  360. };
  361. break;
  362. default:
  363. return -EINVAL;
  364. };
  365. return 0;
  366. }
  367. /*
  368.  * Check if packet should have its dest mac address set to the box itself
  369.  * for diversion
  370.  */
  371. #define ETH_DIVERT_FRAME(skb) 
  372. memcpy(skb->mac.ethernet, skb->dev->dev_addr, ETH_ALEN); 
  373. skb->pkt_type=PACKET_HOST
  374. void divert_frame(struct sk_buff *skb)
  375. {
  376. struct ethhdr *eth = skb->mac.ethernet;
  377. struct iphdr *iph;
  378. struct tcphdr *tcph;
  379. struct udphdr *udph;
  380. struct divert_blk *divert = skb->dev->divert;
  381. int i, src, dst;
  382. unsigned char *skb_data_end = skb->data + skb->len;
  383. /* Packet is already aimed at us, return */
  384. if (!memcmp(eth, skb->dev->dev_addr, ETH_ALEN))
  385. return;
  386. /* proto is not IP, do nothing */
  387. if (eth->h_proto != htons(ETH_P_IP))
  388. return;
  389. /* Divert all IP frames ? */
  390. if (divert->protos & DIVERT_PROTO_IP) {
  391. ETH_DIVERT_FRAME(skb);
  392. return;
  393. }
  394. /* Check for possible (maliciously) malformed IP frame (thanks Dave) */
  395. iph = (struct iphdr *) skb->data;
  396. if (((iph->ihl<<2)+(unsigned char*)(iph)) >= skb_data_end) {
  397. printk(KERN_INFO "divert: malformed IP packet !n");
  398. return;
  399. }
  400. switch (iph->protocol) {
  401. /* Divert all ICMP frames ? */
  402. case IPPROTO_ICMP:
  403. if (divert->protos & DIVERT_PROTO_ICMP) {
  404. ETH_DIVERT_FRAME(skb);
  405. return;
  406. }
  407. break;
  408. /* Divert all TCP frames ? */
  409. case IPPROTO_TCP:
  410. if (divert->protos & DIVERT_PROTO_TCP) {
  411. ETH_DIVERT_FRAME(skb);
  412. return;
  413. }
  414. /* Check for possible (maliciously) malformed IP
  415.  * frame (thanx Dave)
  416.  */
  417. tcph = (struct tcphdr *)
  418. (((unsigned char *)iph) + (iph->ihl<<2));
  419. if (((unsigned char *)(tcph+1)) >= skb_data_end) {
  420. printk(KERN_INFO "divert: malformed TCP packet !n");
  421. return;
  422. }
  423. /* Divert some tcp dst/src ports only ?*/
  424. for (i = 0; i < MAX_DIVERT_PORTS; i++) {
  425. dst = divert->tcp_dst[i];
  426. src = divert->tcp_src[i];
  427. if ((dst && dst == tcph->dest) ||
  428.     (src && src == tcph->source)) {
  429. ETH_DIVERT_FRAME(skb);
  430. return;
  431. }
  432. }
  433. break;
  434. /* Divert all UDP frames ? */
  435. case IPPROTO_UDP:
  436. if (divert->protos & DIVERT_PROTO_UDP) {
  437. ETH_DIVERT_FRAME(skb);
  438. return;
  439. }
  440. /* Check for possible (maliciously) malformed IP
  441.  * packet (thanks Dave)
  442.  */
  443. udph = (struct udphdr *)
  444. (((unsigned char *)iph) + (iph->ihl<<2));
  445. if (((unsigned char *)(udph+1)) >= skb_data_end) {
  446. printk(KERN_INFO
  447.        "divert: malformed UDP packet !n");
  448. return;
  449. }
  450. /* Divert some udp dst/src ports only ? */
  451. for (i = 0; i < MAX_DIVERT_PORTS; i++) {
  452. dst = divert->udp_dst[i];
  453. src = divert->udp_src[i];
  454. if ((dst && dst == udph->dest) ||
  455.     (src && src == udph->source)) {
  456. ETH_DIVERT_FRAME(skb);
  457. return;
  458. }
  459. }
  460. break;
  461. };
  462. return;
  463. }