~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/net/core/dv.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 
 41 const char sysctl_divert_version[32]="0.46";    /* Current version */
 42 
 43 int __init dv_init(void)
 44 {
 45         printk(KERN_INFO "NET4: Frame Diverter %s\n", sysctl_divert_version);
 46         return 0;
 47 }
 48 
 49 /*
 50  * Allocate a divert_blk for a device. This must be an ethernet nic.
 51  */
 52 int alloc_divert_blk(struct net_device *dev)
 53 {
 54         int alloc_size = (sizeof(struct divert_blk) + 3) & ~3;
 55 
 56         if (!strncmp(dev->name, "eth", 3)) {
 57                 printk(KERN_DEBUG "divert: allocating divert_blk for %s\n",
 58                        dev->name);
 59 
 60                 dev->divert = (struct divert_blk *)
 61                         kmalloc(alloc_size, GFP_KERNEL);
 62                 if (dev->divert == NULL) {
 63                         printk(KERN_DEBUG "divert: unable to allocate divert_blk for %s\n",
 64                                dev->name);
 65                         return -ENOMEM;
 66                 } else {
 67                         memset(dev->divert, 0, sizeof(struct divert_blk));
 68                 }
 69         } else {
 70                 printk(KERN_DEBUG "divert: not allocating divert_blk for non-ethernet device %s\n",
 71                        dev->name);
 72 
 73                 dev->divert = NULL;
 74         }
 75         return 0;
 76 } 
 77 
 78 /*
 79  * Free a divert_blk allocated by the above function, if it was 
 80  * allocated on that device.
 81  */
 82 void free_divert_blk(struct net_device *dev)
 83 {
 84         if (dev->divert) {
 85                 kfree(dev->divert);
 86                 dev->divert=NULL;
 87                 printk(KERN_DEBUG "divert: freeing divert_blk for %s\n",
 88                        dev->name);
 89         } else {
 90                 printk(KERN_DEBUG "divert: no divert_blk to free, %s not ethernet\n",
 91                        dev->name);
 92         }
 93 }
 94 
 95 /*
 96  * Adds a tcp/udp (source or dest) port to an array
 97  */
 98 int add_port(u16 ports[], u16 port)
 99 {
100         int i;
101 
102         if (port == 0)
103                 return -EINVAL;
104 
105         /* Storing directly in network format for performance,
106          * thanks Dave :)
107          */
108         port = htons(port);
109 
110         for (i = 0; i < MAX_DIVERT_PORTS; i++) {
111                 if (ports[i] == port)
112                         return -EALREADY;
113         }
114         
115         for (i = 0; i < MAX_DIVERT_PORTS; i++) {
116                 if (ports[i] == 0) {
117                         ports[i] = port;
118                         return 0;
119                 }
120         }
121 
122         return -ENOBUFS;
123 }
124 
125 /*
126  * Removes a port from an array tcp/udp (source or dest)
127  */
128 int remove_port(u16 ports[], u16 port)
129 {
130         int i;
131 
132         if (port == 0)
133                 return -EINVAL;
134         
135         /* Storing directly in network format for performance,
136          * thanks Dave !
137          */
138         port = htons(port);
139 
140         for (i = 0; i < MAX_DIVERT_PORTS; i++) {
141                 if (ports[i] == port) {
142                         ports[i] = 0;
143                         return 0;
144                 }
145         }
146 
147         return -EINVAL;
148 }
149 
150 /* Some basic sanity checks on the arguments passed to divert_ioctl() */
151 int check_args(struct divert_cf *div_cf, struct net_device **dev)
152 {
153         char devname[32];
154                 
155         if (dev == NULL)
156                 return -EFAULT;
157         
158         /* GETVERSION: all other args are unused */
159         if (div_cf->cmd == DIVCMD_GETVERSION)
160                 return 0;
161         
162         /* Network device index should reasonably be between 0 and 1000 :) */
163         if (div_cf->dev_index < 0 || div_cf->dev_index > 1000) 
164                 return -EINVAL;
165                         
166         /* Let's try to find the ifname */
167         sprintf(devname, "eth%d", div_cf->dev_index);
168         *dev = dev_get_by_name(devname);
169         
170         /* dev should NOT be null */
171         if (*dev == NULL)
172                 return -EINVAL;
173         
174         /* user issuing the ioctl must be a super one :) */
175         if (!suser())
176                 return -EPERM;
177 
178         /* Device must have a divert_blk member NOT null */
179         if ((*dev)->divert == NULL)
180                 return -EFAULT;
181 
182         return 0;
183 }
184 
185 /*
186  * control function of the diverter
187  */
188 #define DVDBG(a)        \
189         printk(KERN_DEBUG "divert_ioctl() line %d %s\n", __LINE__, (a))
190 
191 int divert_ioctl(unsigned int cmd, struct divert_cf *arg)
192 {
193         struct divert_cf        div_cf;
194         struct divert_blk       *div_blk;
195         struct net_device       *dev;
196         int                     ret;
197 
198         switch (cmd) {
199         case SIOCGIFDIVERT:
200                 DVDBG("SIOCGIFDIVERT, copy_from_user");
201                 if (copy_from_user(&div_cf, arg, sizeof(struct divert_cf)))
202                         return -EFAULT;
203                 DVDBG("before check_args");
204                 ret = check_args(&div_cf, &dev);
205                 if (ret)
206                         return ret;
207                 DVDBG("after checkargs");
208                 div_blk = dev->divert;
209                         
210                 DVDBG("befre switch()");
211                 switch (div_cf.cmd) {
212                 case DIVCMD_GETSTATUS:
213                         /* Now, just give the user the raw divert block
214                          * for him to play with :)
215                          */
216                         if (copy_to_user(div_cf.arg1.ptr, dev->divert,
217                                          sizeof(struct divert_blk)))
218                                 return -EFAULT;
219                         break;
220 
221                 case DIVCMD_GETVERSION:
222                         DVDBG("GETVERSION: checking ptr");
223                         if (div_cf.arg1.ptr == NULL)
224                                 return -EINVAL;
225                         DVDBG("GETVERSION: copying data to userland");
226                         if (copy_to_user(div_cf.arg1.ptr,
227                                          sysctl_divert_version, 32))
228                                 return -EFAULT;
229                         DVDBG("GETVERSION: data copied");
230                         break;
231 
232                 default:
233                         return -EINVAL;
234                 };
235 
236                 break;
237 
238         case SIOCSIFDIVERT:
239                 if (copy_from_user(&div_cf, arg, sizeof(struct divert_cf)))
240                         return -EFAULT;
241 
242                 ret = check_args(&div_cf, &dev);
243                 if (ret)
244                         return ret;
245 
246                 div_blk = dev->divert;
247 
248                 switch(div_cf.cmd) {
249                 case DIVCMD_RESET:
250                         div_blk->divert = 0;
251                         div_blk->protos = DIVERT_PROTO_NONE;
252                         memset(div_blk->tcp_dst, 0,
253                                MAX_DIVERT_PORTS * sizeof(u16));
254                         memset(div_blk->tcp_src, 0,
255                                MAX_DIVERT_PORTS * sizeof(u16));
256                         memset(div_blk->udp_dst, 0,
257                                MAX_DIVERT_PORTS * sizeof(u16));
258                         memset(div_blk->udp_src, 0,
259                                MAX_DIVERT_PORTS * sizeof(u16));
260                         return 0;
261                                 
262                 case DIVCMD_DIVERT:
263                         switch(div_cf.arg1.int32) {
264                         case DIVARG1_ENABLE:
265                                 if (div_blk->divert)
266                                         return -EALREADY;
267                                 div_blk->divert = 1;
268                                 break;
269 
270                         case DIVARG1_DISABLE:
271                                 if (!div_blk->divert)
272                                         return -EALREADY;
273                                 div_blk->divert = 0;
274                                 break;
275 
276                         default:
277                                 return -EINVAL;
278                         };
279 
280                         break;
281 
282                 case DIVCMD_IP:
283                         switch(div_cf.arg1.int32) {
284                         case DIVARG1_ENABLE:
285                                 if (div_blk->protos & DIVERT_PROTO_IP)
286                                         return -EALREADY;
287                                 div_blk->protos |= DIVERT_PROTO_IP;
288                                 break;
289 
290                         case DIVARG1_DISABLE:
291                                 if (!(div_blk->protos & DIVERT_PROTO_IP))
292                                         return -EALREADY;
293                                 div_blk->protos &= ~DIVERT_PROTO_IP;
294                                 break;
295 
296                         default:
297                                 return -EINVAL;
298                         };
299 
300                         break;
301 
302                 case DIVCMD_TCP:
303                         switch(div_cf.arg1.int32) {
304                         case DIVARG1_ENABLE:
305                                 if (div_blk->protos & DIVERT_PROTO_TCP)
306                                         return -EALREADY;
307                                 div_blk->protos |= DIVERT_PROTO_TCP;
308                                 break;
309 
310                         case DIVARG1_DISABLE:
311                                 if (!(div_blk->protos & DIVERT_PROTO_TCP))
312                                         return -EALREADY;
313                                 div_blk->protos &= ~DIVERT_PROTO_TCP;
314                                 break;
315 
316                         default:
317                                 return -EINVAL;
318                         };
319 
320                         break;
321 
322                 case DIVCMD_TCPDST:
323                         switch(div_cf.arg1.int32) {
324                         case DIVARG1_ADD:
325                                 return add_port(div_blk->tcp_dst,
326                                                 div_cf.arg2.uint16);
327                                 
328                         case DIVARG1_REMOVE:
329                                 return remove_port(div_blk->tcp_dst,
330                                                    div_cf.arg2.uint16);
331 
332                         default:
333                                 return -EINVAL;
334                         };
335 
336                         break;
337 
338                 case DIVCMD_TCPSRC:
339                         switch(div_cf.arg1.int32) {
340                         case DIVARG1_ADD:
341                                 return add_port(div_blk->tcp_src,
342                                                 div_cf.arg2.uint16);
343 
344                         case DIVARG1_REMOVE:
345                                 return remove_port(div_blk->tcp_src,
346                                                    div_cf.arg2.uint16);
347 
348                         default:
349                                 return -EINVAL;
350                         };
351 
352                         break;
353 
354                 case DIVCMD_UDP:
355                         switch(div_cf.arg1.int32) {
356                         case DIVARG1_ENABLE:
357                                 if (div_blk->protos & DIVERT_PROTO_UDP)
358                                         return -EALREADY;
359                                 div_blk->protos |= DIVERT_PROTO_UDP;
360                                 break;
361 
362                         case DIVARG1_DISABLE:
363                                 if (!(div_blk->protos & DIVERT_PROTO_UDP))
364                                         return -EALREADY;
365                                 div_blk->protos &= ~DIVERT_PROTO_UDP;
366                                 break;
367 
368                         default:
369                                 return -EINVAL;
370                         };
371 
372                         break;
373 
374                 case DIVCMD_UDPDST:
375                         switch(div_cf.arg1.int32) {
376                         case DIVARG1_ADD:
377                                 return add_port(div_blk->udp_dst,
378                                                 div_cf.arg2.uint16);
379 
380                         case DIVARG1_REMOVE:
381                                 return remove_port(div_blk->udp_dst,
382                                                    div_cf.arg2.uint16);
383 
384                         default:
385                                 return -EINVAL;
386                         };
387 
388                         break;
389 
390                 case DIVCMD_UDPSRC:
391                         switch(div_cf.arg1.int32) {
392                         case DIVARG1_ADD:
393                                 return add_port(div_blk->udp_src,
394                                                 div_cf.arg2.uint16);
395 
396                         case DIVARG1_REMOVE:
397                                 return remove_port(div_blk->udp_src,
398                                                    div_cf.arg2.uint16);
399 
400                         default:
401                                 return -EINVAL;
402                         };
403 
404                         break;
405 
406                 case DIVCMD_ICMP:
407                         switch(div_cf.arg1.int32) {
408                         case DIVARG1_ENABLE:
409                                 if (div_blk->protos & DIVERT_PROTO_ICMP)
410                                         return -EALREADY;
411                                 div_blk->protos |= DIVERT_PROTO_ICMP;
412                                 break;
413 
414                         case DIVARG1_DISABLE:
415                                 if (!(div_blk->protos & DIVERT_PROTO_ICMP))
416                                         return -EALREADY;
417                                 div_blk->protos &= ~DIVERT_PROTO_ICMP;
418                                 break;
419 
420                         default:
421                                 return -EINVAL;
422                         };
423 
424                         break;
425 
426                 default:
427                         return -EINVAL;
428                 };
429 
430                 break;
431 
432         default:
433                 return -EINVAL;
434         };
435 
436         return 0;
437 }
438 
439 
440 /*
441  * Check if packet should have its dest mac address set to the box itself
442  * for diversion
443  */
444 
445 #define ETH_DIVERT_FRAME(skb) \
446         memcpy(skb->mac.ethernet, skb->dev->dev_addr, ETH_ALEN); \
447         skb->pkt_type=PACKET_HOST
448                 
449 void divert_frame(struct sk_buff *skb)
450 {
451         struct ethhdr                   *eth = skb->mac.ethernet;
452         struct iphdr                    *iph;
453         struct tcphdr                   *tcph;
454         struct udphdr                   *udph;
455         struct divert_blk               *divert = skb->dev->divert;
456         int                             i, src, dst;
457         unsigned char                   *skb_data_end = skb->data + skb->len;
458 
459         /* Packet is already aimed at us, return */
460         if (!memcmp(eth, skb->dev->dev_addr, ETH_ALEN))
461                 return;
462         
463         /* proto is not IP, do nothing */
464         if (eth->h_proto != htons(ETH_P_IP))
465                 return;
466         
467         /* Divert all IP frames ? */
468         if (divert->protos & DIVERT_PROTO_IP) {
469                 ETH_DIVERT_FRAME(skb);
470                 return;
471         }
472         
473         /* Check for possible (maliciously) malformed IP frame (thanks Dave) */
474         iph = (struct iphdr *) skb->data;
475         if (((iph->ihl<<2)+(unsigned char*)(iph)) >= skb_data_end) {
476                 printk(KERN_INFO "divert: malformed IP packet !\n");
477                 return;
478         }
479 
480         switch (iph->protocol) {
481         /* Divert all ICMP frames ? */
482         case IPPROTO_ICMP:
483                 if (divert->protos & DIVERT_PROTO_ICMP) {
484                         ETH_DIVERT_FRAME(skb);
485                         return;
486                 }
487                 break;
488 
489         /* Divert all TCP frames ? */
490         case IPPROTO_TCP:
491                 if (divert->protos & DIVERT_PROTO_TCP) {
492                         ETH_DIVERT_FRAME(skb);
493                         return;
494                 }
495 
496                 /* Check for possible (maliciously) malformed IP
497                  * frame (thanx Dave)
498                  */
499                 tcph = (struct tcphdr *)
500                         (((unsigned char *)iph) + (iph->ihl<<2));
501                 if (((unsigned char *)(tcph+1)) >= skb_data_end) {
502                         printk(KERN_INFO "divert: malformed TCP packet !\n");
503                         return;
504                 }
505 
506                 /* Divert some tcp dst/src ports only ?*/
507                 for (i = 0; i < MAX_DIVERT_PORTS; i++) {
508                         dst = divert->tcp_dst[i];
509                         src = divert->tcp_src[i];
510                         if ((dst && dst == tcph->dest) ||
511                             (src && src == tcph->source)) {
512                                 ETH_DIVERT_FRAME(skb);
513                                 return;
514                         }
515                 }
516                 break;
517 
518         /* Divert all UDP frames ? */
519         case IPPROTO_UDP:
520                 if (divert->protos & DIVERT_PROTO_UDP) {
521                         ETH_DIVERT_FRAME(skb);
522                         return;
523                 }
524 
525                 /* Check for possible (maliciously) malformed IP
526                  * packet (thanks Dave)
527                  */
528                 udph = (struct udphdr *)
529                         (((unsigned char *)iph) + (iph->ihl<<2));
530                 if (((unsigned char *)(udph+1)) >= skb_data_end) {
531                         printk(KERN_INFO
532                                "divert: malformed UDP packet !\n");
533                         return;
534                 }
535 
536                 /* Divert some udp dst/src ports only ? */
537                 for (i = 0; i < MAX_DIVERT_PORTS; i++) {
538                         dst = divert->udp_dst[i];
539                         src = divert->udp_src[i];
540                         if ((dst && dst == udph->dest) ||
541                             (src && src == udph->source)) {
542                                 ETH_DIVERT_FRAME(skb);
543                                 return;
544                         }
545                 }
546                 break;
547         };
548 
549         return;
550 }
551 
552 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.