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

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

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

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

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp