patch-1.3.81 linux/net/ipv4/ip_fw.c
Next file: linux/net/ipv4/ip_input.c
Previous file: linux/net/ipv4/ip_forward.c
Back to the patch index
Back to the overall index
- Lines: 658
- Date:
Sat Mar 30 13:20:34 1996
- Orig file:
v1.3.80/linux/net/ipv4/ip_fw.c
- Orig date:
Tue Mar 19 12:51:42 1996
diff -u --recursive --new-file v1.3.80/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
@@ -34,6 +34,7 @@
* Add support for matching on device names.
* Jos Vos <jos@xos.nl> 15/2/1996.
*
+ *
* Masquerading functionality
*
* Copyright (c) 1994 Pauline Middelink
@@ -52,6 +53,8 @@
* Alan Cox : Cleaned up length setting.
* Wouter Gadeyne : Fixed masquerading support of ftp PORT commands
*
+ * Juan Jose Ciarlante : Masquerading code moved to ip_masq.c
+ *
* All the real work was done by .....
*
*/
@@ -98,6 +101,11 @@
#include <net/icmp.h>
#include <linux/firewall.h>
#include <linux/ip_fw.h>
+
+#ifdef CONFIG_IP_MASQUERADE
+#include <net/ip_masq.h>
+#endif
+
#include <net/checksum.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
@@ -148,17 +156,6 @@
#endif
-#ifdef CONFIG_IP_MASQUERADE
-/*
- * Implement IP packet masquerading
- */
-
-static unsigned short masq_port = PORT_MASQ_BEGIN;
-static const char *strProt[] = {"UDP","TCP"};
-struct ip_masq *ip_msq_hosts;
-
-#endif
-
/*
* Returns 1 if the port is matched by the vector, 0 otherwise
*/
@@ -522,517 +519,6 @@
return 0;
}
-#ifdef CONFIG_IP_MASQUERADE
-
-static void masq_expire(unsigned long data)
-{
- struct ip_masq *ms = (struct ip_masq *)data;
- struct ip_masq *old,*cur;
- unsigned long flags;
-
-#ifdef DEBUG_MASQ
- printk("Masqueraded %s %lX:%X expired\n",
- strProt[ms->protocol==IPPROTO_TCP],
- ntohl(ms->src),ntohs(ms->sport));
-#endif
-
- save_flags(flags);
- cli();
-
- /* delete from list of hosts */
- old = NULL;
- cur = ip_msq_hosts;
- while (cur!=NULL) {
- if (cur==ms) {
- if (old==NULL) ip_msq_hosts = ms->next;
- else old->next = ms->next;
- kfree_s(ms,sizeof(*ms));
- break;
- }
- old = cur;
- cur=cur->next;
- }
- restore_flags(flags);
-}
-
-/*
- * Create a new masquerade list entry, also allocate an
- * unused mport, keeping the portnumber between the
- * given boundaries MASQ_BEGIN and MASQ_END.
- *
- * FIXME: possible deadlock if all free ports are exhausted!
- */
-static struct ip_masq *alloc_masq_entry(void)
-{
- struct ip_masq *ms, *mst;
- unsigned long flags;
-
- ms = (struct ip_masq *) kmalloc(sizeof(struct ip_masq), GFP_ATOMIC);
- if (ms==NULL)
- return NULL;
-
- memset(ms,0,sizeof(*ms));
- init_timer(&ms->timer);
- ms->timer.data = (unsigned long)ms;
- ms->timer.function = masq_expire;
-
- save_flags(flags);
- cli();
- do
- {
- /* Try the next available port number */
- ms->mport = htons(masq_port++);
- if (masq_port==PORT_MASQ_END)
- masq_port = PORT_MASQ_BEGIN;
-
- /* Now hunt through the used ports to see if
- * this port is in use... */
- mst = ip_msq_hosts;
- while (mst && mst->mport!=ms->mport)
- mst = mst->next;
- }
- while (mst!=NULL);
-
- /* add new entry in front of list to minimize lookup-time */
- ms->next = ip_msq_hosts;
- ip_msq_hosts = ms;
- restore_flags(flags);
-
- return ms;
-}
-
-/*
- * When passing an FTP 'PORT' command, try to replace the IP
- * address with an newly assigned (masquereded) port on this
- * host, so the ftp-data connect FROM the site will succeed...
- *
- * Also, when the size of the packet changes, create an delta
- * offset, which will be added to every th->seq (and subtracted for
- * (th->acqseq) whose seq > init_seq.
- *
- * Not for the faint of heart!
- */
-
-static struct sk_buff *revamp(struct sk_buff *skb, struct device *dev, struct ip_masq *ftp)
-{
- struct iphdr *iph = skb->h.iph;
- struct tcphdr *th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
- struct sk_buff *skb2;
- char *p, *data = (char *)&th[1];
- unsigned char p1,p2,p3,p4,p5,p6;
- unsigned long from;
- unsigned short port;
- struct ip_masq *ms;
- char buf[24]; /* xxx.xxx.xxx.xxx,ppp,ppp\000 */
- int diff;
- __u32 seq;
-
- /*
- * Adjust seq with delta-offset for all packets after the most recent resized PORT command
- * and with previous_delta offset for all packets before most recent resized PORT
- */
-
- /*
- * seq & seq_ack are in network byte order; need conversion before comparing
- */
- seq=ntohl(th->seq);
- if (ftp->delta || ftp->previous_delta)
- {
- if(after(seq,ftp->init_seq) )
- {
- th->seq = htonl(seq + ftp->delta);
-#ifdef DEBUG_MASQ
- printk("masq_revamp : added delta (%d) to seq\n",ftp->delta);
-#endif
- }
- else
- {
- th->seq = htonl(seq + ftp->previous_delta);
-#ifdef DEBUG_MASQ
- printk("masq_revamp : added previous_delta (%d) to seq\n",ftp->previous_delta);
-#endif
- }
- }
-
- while (skb->len - ((unsigned char *)data - skb->h.raw) > 18)
- {
- if (memcmp(data,"PORT ",5) && memcmp(data,"port ",5))
- {
- data ++;
- continue;
- }
- p = data+5;
- p1 = simple_strtoul(data+5,&data,10);
- if (*data!=',')
- continue;
- p2 = simple_strtoul(data+1,&data,10);
- if (*data!=',')
- continue;
- p3 = simple_strtoul(data+1,&data,10);
- if (*data!=',')
- continue;
- p4 = simple_strtoul(data+1,&data,10);
- if (*data!=',')
- continue;
- p5 = simple_strtoul(data+1,&data,10);
- if (*data!=',')
- continue;
- p6 = simple_strtoul(data+1,&data,10);
- if (*data!='\r' && *data!='\n')
- continue;
-
- from = (p1<<24) | (p2<<16) | (p3<<8) | p4;
- port = (p5<<8) | p6;
-#ifdef MASQ_DEBUG
- printk("PORT %lX:%X detected\n",from,port);
-#endif
- /*
- * Now create an masquerade entry for it
- */
- ms = alloc_masq_entry();
- if (ms==NULL)
- return skb;
- ms->protocol = IPPROTO_TCP;
- ms->src = htonl(from); /* derived from PORT cmd */
- ms->sport = htons(port); /* derived from PORT cmd */
- ms->dst = iph->daddr;
- /*
- * Hardcoding 20 as dport is not always correct
- * At least 1 Windows ftpd uses a random port number instead of 20
- * Leave it undefined for now & wait for the first connection request to fill it out
- */
- ms->dport = htons(FTP_DPORT_TBD); /* ftp-data */
- ms->timer.expires = jiffies+MASQUERADE_EXPIRE_TCP_FIN;
- add_timer(&ms->timer);
-
- /*
- * Replace the old PORT with the new one
- */
- from = ntohl(dev->pa_addr);
- port = ntohs(ms->mport);
- sprintf(buf,"%ld,%ld,%ld,%ld,%d,%d",
- from>>24&255,from>>16&255,from>>8&255,from&255,
- port>>8&255,port&255);
-
- /*
- * Calculate required delta-offset to keep TCP happy
- */
-
- diff = strlen(buf) - (data-p);
-
- /*
- * No shift.
- */
-
- if (diff==0)
- {
- /*
- * simple case, just replace the old PORT cmd
- */
- memcpy(p,buf,strlen(buf));
- return skb;
- }
-
- /*
- * If the PORT command we have fiddled is the first, or is a
- * resend don't do the delta shift again. Doesn't work for
- * pathological cases, but we would need a history for that.
- * Also fails if you send 2^31 bytes of data down the link
- * after the first port command.
- *
- * FIXME: use ftp->init_seq_valid - 0 is a valid sequence.
- */
-
- if(!ftp->init_seq || after(seq,ftp->init_seq) )
- {
- ftp->previous_delta=ftp->delta;
- ftp->delta+=diff;
- ftp->init_seq = seq;
- }
-
- /*
- * Sizes differ, make a copy
- */
-#ifdef DEBUG_MASQ
- printk("MASQUERADE: resizing needed for %d bytes (%ld)\n",diff, skb->len);
-#endif
- skb2 = alloc_skb(MAX_HEADER + skb->len+diff, GFP_ATOMIC);
- if (skb2 == NULL) {
- printk("MASQUERADE: No memory available\n");
- return skb;
- }
- skb2->free = skb->free;
- skb_reserve(skb2,MAX_HEADER);
- skb_put(skb2,skb->len + diff);
- skb2->h.raw = skb2->data + (skb->h.raw - skb->data);
- iph=skb2->h.iph;
- /*
- * Mend the IP header too
- */
- iph->tot_len = htons(diff+ntohs(iph->tot_len));
- iph->check = 0;
- iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
-
- /*
- * Copy the packet data into the new buffer.
- * Thereby replacing the PORT cmd.
- */
- memcpy(skb2->data, skb->data, (p - (char *)skb->data));
- memcpy(&skb2->data[(p - (char *)skb->data)], buf, strlen(buf));
- memcpy(&skb2->data[(p - (char *)skb->data) + strlen(buf)], data,
- skb->len - (data-(char *)skb->data));
-
- /*
- * Update tot_len field in ip header !
- * Sequence numbers were allready modified in original packet
- */
- iph->tot_len = htons(skb->len + diff);
-
- /*
- * Problem, how to replace the new skb with old one,
- * preferably inplace, so all the pointers in the
- * calling tree keep ok :(
- */
- kfree_skb(skb, FREE_WRITE);
- return skb2;
- }
- return skb;
-}
-
-static void recalc_check(struct udphdr *uh, unsigned long saddr,
- unsigned long daddr, int len)
-{
- uh->check=0;
- uh->check=csum_tcpudp_magic(saddr,daddr,len,
- IPPROTO_UDP, csum_partial((char *)uh,len,0));
- if(uh->check==0)
- uh->check=0xFFFF;
-}
-
-void ip_fw_masquerade(struct sk_buff **skb_ptr, struct device *dev)
-{
- struct sk_buff *skb=*skb_ptr;
- struct iphdr *iph = skb->h.iph;
- unsigned short *portptr;
- struct ip_masq *ms;
- int size;
-
- /*
- * We can only masquerade protocols with ports...
- */
-
- if (iph->protocol!=IPPROTO_UDP && iph->protocol!=IPPROTO_TCP)
- return;
-
- /*
- * Now hunt the list to see if we have an old entry
- */
-
- portptr = (unsigned short *)&(((char *)iph)[iph->ihl*4]);
- ms = ip_msq_hosts;
-
-#ifdef DEBUG_MASQ
- printk("Outgoing %s %lX:%X -> %lX:%X\n",
- strProt[iph->protocol==IPPROTO_TCP],
- ntohl(iph->saddr), ntohs(portptr[0]),
- ntohl(iph->daddr), ntohs(portptr[1]));
-#endif
- while (ms!=NULL)
- {
- if (iph->protocol == ms->protocol &&
- iph->saddr == ms->src && iph->daddr == ms->dst &&
- portptr[0] == ms->sport && portptr[1] == ms->dport)
- {
- del_timer(&ms->timer);
- break;
- }
- ms = ms->next;
- }
-
- /*
- * Nope, not found, create a new entry for it
- */
-
- if (ms==NULL)
- {
- ms = alloc_masq_entry();
- if (ms==NULL)
- {
- printk("MASQUERADE: no memory left !\n");
- return;
- }
- ms->protocol = iph->protocol;
- ms->src = iph->saddr;
- ms->dst = iph->daddr;
- ms->sport = portptr[0];
- ms->dport = portptr[1];
- }
-
- /*
- * Change the fragments origin
- */
-
- size = skb->len - ((unsigned char *)portptr - skb->h.raw);
- iph->saddr = dev->pa_addr; /* my own address */
- portptr[0] = ms->mport;
-
- /*
- * Adjust packet accordingly to protocol
- */
-
- if (iph->protocol==IPPROTO_UDP)
- {
- ms->timer.expires = jiffies+MASQUERADE_EXPIRE_UDP;
- recalc_check((struct udphdr *)portptr,iph->saddr,iph->daddr,size);
- }
- else
- {
- struct tcphdr *th;
- if (portptr[1]==htons(21))
- {
- skb = revamp(*skb_ptr, dev, ms);
- *skb_ptr = skb;
- iph = skb->h.iph;
- portptr = (unsigned short *)&(((char *)iph)[iph->ihl*4]);
- size = skb->len - ((unsigned char *)portptr-skb->h.raw);
- }
- th = (struct tcphdr *)portptr;
-
- /*
- * Timeout depends if FIN packet was seen
- */
- if (ms->sawfin || th->fin)
- {
- ms->timer.expires = jiffies+MASQUERADE_EXPIRE_TCP_FIN;
- ms->sawfin = 1;
- }
- else ms->timer.expires = jiffies+MASQUERADE_EXPIRE_TCP;
-
- skb->csum = csum_partial((void *)(th + 1), size - sizeof(*th), 0);
- tcp_send_check(th,iph->saddr,iph->daddr,size,skb);
- }
- add_timer(&ms->timer);
- ip_send_check(iph);
-
- #ifdef DEBUG_MASQ
- printk("O-routed from %lX:%X over %s\n",ntohl(dev->pa_addr),ntohs(ms->mport),dev->name);
- #endif
- }
-
- /*
- * Check if it's an masqueraded port, look it up,
- * and send it on it's way...
- *
- * Better not have many hosts using the designated portrange
- * as 'normal' ports, or you'll be spending lots of time in
- * this function.
- */
-
-int ip_fw_demasquerade(struct sk_buff *skb)
-{
- struct iphdr *iph = skb->h.iph;
- unsigned short *portptr;
- struct ip_masq *ms;
- struct tcphdr *th = (struct tcphdr *)(skb->h.raw+(iph->ihl<<2));
-
- if (iph->protocol!=IPPROTO_UDP && iph->protocol!=IPPROTO_TCP)
- return 0;
-
- portptr = (unsigned short *)&(((char *)iph)[iph->ihl*4]);
- if (ntohs(portptr[1]) < PORT_MASQ_BEGIN ||
- ntohs(portptr[1]) > PORT_MASQ_END)
- return 0;
-
-#ifdef DEBUG_MASQ
- printk("Incoming %s %lX:%X -> %lX:%X\n",
- strProt[iph->protocol==IPPROTO_TCP],
- ntohl(iph->saddr), ntohs(portptr[0]),
- ntohl(iph->daddr), ntohs(portptr[1]));
-#endif
-
- /*
- * reroute to original host:port if found...
- *
- * NB. Cannot check destination address, just for the incoming port.
- * reason: archie.doc.ac.uk has 6 interfaces, you send to
- * phoenix and get a reply from any other interface(==dst)!
- *
- * [Only for UDP] - AC
- */
- ms = ip_msq_hosts;
- while (ms!=NULL)
- {
- if (iph->protocol==ms->protocol &&
- (iph->saddr==ms->dst || iph->protocol==IPPROTO_UDP) &&
- (ms->dport==htons(FTP_DPORT_TBD) || portptr[0]==ms->dport) &&
- portptr[1]==ms->mport)
- {
-
- int size = skb->len - ((unsigned char *)portptr - skb->h.raw);
- iph->daddr = ms->src;
- portptr[1] = ms->sport;
-
- if(ms->dport==htons(FTP_DPORT_TBD))
- {
- ms->dport=portptr[0];
-#ifdef DEBUG_MASQ
- printk("demasq : Filled out dport entry (%d) based on initial connect attempt from FTP deamon\n",ntohs(ms->dport));
-#endif
- }
-
- /*
- * Yug! adjust UDP/TCP and IP checksums
- */
- if (iph->protocol==IPPROTO_UDP)
- recalc_check((struct udphdr *)portptr,iph->saddr,iph->daddr,size);
- else
- {
- __u32 ack_seq;
- /*
- * Adjust ack_seq with delta-offset for
- * the packets AFTER most recent PORT command has caused a shift
- * for packets before most recent PORT command, use previous_delta
- */
-#ifdef DEBUG_MASQ
- printk("demasq : delta=%d ; previous_delta=%d ; init_seq=%lX ; ack_seq=%lX ; after=%d\n",ms->delta,ms->previous_delta,ntohl(ms->init_seq),ntohl(th->ack_seq),after(ntohl(th->ack_seq),ntohl(ms->init_seq)));
-#endif
- ack_seq=ntohl(th->ack_seq);
- if (ms->delta || ms->previous_delta)
- {
- if(after(ack_seq,ms->init_seq))
- {
- th->ack_seq = htonl(ack_seq-ms->delta);
-#ifdef DEBUG_MASQ
- printk("demasq : substracted delta (%d) from ack_seq\n",ms->delta);
-#endif
- }
- else
- {
- th->ack_seq = htonl(ack_seq-ms->previous_delta);
-#ifdef DEBUG_MASQ
- printk("demasq : substracted previous_delta (%d) from ack_seq\n",ms->previous_delta);
-#endif
- }
- }
- skb->csum = csum_partial((void *)(((struct tcphdr *)portptr) + 1),
- size - sizeof(struct tcphdr), 0);
- tcp_send_check((struct tcphdr *)portptr,iph->saddr,iph->daddr,size,skb);
- }
- ip_send_check(iph);
-#ifdef DEBUG_MASQ
- printk("I-routed to %lX:%X\n",ntohl(iph->daddr),ntohs(portptr[1]));
-#endif
- return 1;
- }
- ms = ms->next;
- }
-
- /* sorry, all this trouble for a no-hit :) */
- return 0;
-}
-#endif
-
-
static void zero_fw_chain(struct ip_fw *chainptr)
{
@@ -1079,8 +565,12 @@
}
memcpy(ftmp, frwl, len);
- ftmp->fw_tosand |= 0x03;
- ftmp->fw_tosxor &= 0xFC;
+ /*
+ * Allow the more recent "minimise cost" flag to be
+ * set. [Rob van Nieuwkerk]
+ */
+ ftmp->fw_tosand |= 0x01;
+ ftmp->fw_tosxor &= 0xFE;
ftmp->fw_pcnt=0L;
ftmp->fw_bcnt=0L;
@@ -1567,54 +1057,6 @@
}
#endif
-#ifdef CONFIG_IP_MASQUERADE
-
-static int ip_msqhst_procinfo(char *buffer, char **start, off_t offset,
- int length, int unused)
-{
- off_t pos=0, begin=0;
- struct ip_masq *ms;
- unsigned long flags;
- int len=0;
-
- len=sprintf(buffer,"Prc FromIP FPrt ToIP TPrt Masq Init-seq Delta PDelta Expires\n");
- save_flags(flags);
- cli();
-
- ms=ip_msq_hosts;
- while (ms!=NULL)
- {
- int timer_active = del_timer(&ms->timer);
- if (!timer_active)
- ms->timer.expires = jiffies;
- len+=sprintf(buffer+len,"%s %08lX:%04X %08lX:%04X %04X %08X %6d %6d %lu\n",
- strProt[ms->protocol==IPPROTO_TCP],
- ntohl(ms->src),ntohs(ms->sport),
- ntohl(ms->dst),ntohs(ms->dport),
- ntohs(ms->mport),
- ms->init_seq,ms->delta,ms->previous_delta,ms->timer.expires-jiffies);
- if (timer_active)
- add_timer(&ms->timer);
-
- pos=begin+len;
- if(pos<offset)
- {
- len=0;
- begin=pos;
- }
- if(pos>offset+length)
- break;
- ms=ms->next;
- }
- restore_flags(flags);
- *start=buffer+(offset-begin);
- len-=(offset-begin);
- if(len>length)
- len=length;
- return len;
-}
-
-#endif
#ifdef CONFIG_IP_FIREWALL
/*
@@ -1722,14 +1164,16 @@
ip_fw_fwd_procinfo
});
#endif
+
#ifdef CONFIG_IP_MASQUERADE
- proc_net_register(&(struct proc_dir_entry) {
- PROC_NET_IPMSQHST, 13, "ip_masquerade",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- ip_msqhst_procinfo
- });
+
+ /*
+ * Initialize masquerading.
+ */
+
+ ip_masq_init();
#endif
+
#if defined(CONFIG_IP_ACCT) || defined(CONFIG_IP_FIREWALL)
/* Register for device up/down reports */
register_netdevice_notifier(&ipfw_dev_notifier);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this