summaryrefslogtreecommitdiff
path: root/pfinet.old/linux-src/net/ipv4/ip_output.c~
diff options
context:
space:
mode:
Diffstat (limited to 'pfinet.old/linux-src/net/ipv4/ip_output.c~')
-rw-r--r--pfinet.old/linux-src/net/ipv4/ip_output.c~1000
1 files changed, 0 insertions, 1000 deletions
diff --git a/pfinet.old/linux-src/net/ipv4/ip_output.c~ b/pfinet.old/linux-src/net/ipv4/ip_output.c~
deleted file mode 100644
index 89272d6b..00000000
--- a/pfinet.old/linux-src/net/ipv4/ip_output.c~
+++ /dev/null
@@ -1,1000 +0,0 @@
-/*
- * INET An implementation of the TCP/IP protocol suite for the LINUX
- * operating system. INET is implemented using the BSD Socket
- * interface as the means of communication with the user level.
- *
- * The Internet Protocol (IP) output module.
- *
- * Version: $Id: ip_output.c,v 1.67.2.1 1999/09/07 02:25:23 davem Exp $
- *
- * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
- * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
- * Donald Becker, <becker@super.org>
- * Alan Cox, <Alan.Cox@linux.org>
- * Richard Underwood
- * Stefan Becker, <stefanb@yello.ping.de>
- * Jorge Cwik, <jorge@laser.satlink.net>
- * Arnt Gulbrandsen, <agulbra@nvg.unit.no>
- *
- * See ip_input.c for original log
- *
- * Fixes:
- * Alan Cox : Missing nonblock feature in ip_build_xmit.
- * Mike Kilburn : htons() missing in ip_build_xmit.
- * Bradford Johnson: Fix faulty handling of some frames when
- * no route is found.
- * Alexander Demenshin: Missing sk/skb free in ip_queue_xmit
- * (in case if packet not accepted by
- * output firewall rules)
- * Mike McLagan : Routing by source
- * Alexey Kuznetsov: use new route cache
- * Andi Kleen: Fix broken PMTU recovery and remove
- * some redundant tests.
- * Vitaly E. Lavrov : Transparent proxy revived after year coma.
- * Andi Kleen : Replace ip_reply with ip_send_reply.
- * Andi Kleen : Split fast and slow ip_build_xmit path
- * for decreased register pressure on x86
- * and more readibility.
- * Marc Boucher : When call_out_firewall returns FW_QUEUE,
- * silently drop skb instead of failing with -EPERM.
- */
-
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/config.h>
-
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/in.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
-#include <linux/init.h>
-
-#include <net/snmp.h>
-#include <net/ip.h>
-#include <net/protocol.h>
-#include <net/route.h>
-#include <net/tcp.h>
-#include <net/udp.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <net/arp.h>
-#include <net/icmp.h>
-#include <net/raw.h>
-#include <net/checksum.h>
-#include <linux/igmp.h>
-#include <linux/ip_fw.h>
-#include <linux/firewall.h>
-#include <linux/mroute.h>
-#include <linux/netlink.h>
-
-/*
- * Shall we try to damage output packets if routing dev changes?
- */
-
-int sysctl_ip_dynaddr = 0;
-
-
-int ip_id_count = 0;
-
-/* Generate a checksum for an outgoing IP datagram. */
-__inline__ void ip_send_check(struct iphdr *iph)
-{
- iph->check = 0;
- iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
-}
-
-/*
- * Add an ip header to a skbuff and send it out.
- */
-void ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
- u32 saddr, u32 daddr, struct ip_options *opt)
-{
- struct rtable *rt = (struct rtable *)skb->dst;
- struct iphdr *iph;
- struct device *dev;
-
- /* Build the IP header. */
- if (opt)
- iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr) + opt->optlen);
- else
- iph=(struct iphdr *)skb_push(skb,sizeof(struct iphdr));
-
- iph->version = 4;
- iph->ihl = 5;
- iph->tos = sk->ip_tos;
- iph->frag_off = 0;
- if (ip_dont_fragment(sk, &rt->u.dst))
- iph->frag_off |= htons(IP_DF);
- iph->ttl = sk->ip_ttl;
- iph->daddr = rt->rt_dst;
- iph->saddr = rt->rt_src;
- iph->protocol = sk->protocol;
- iph->tot_len = htons(skb->len);
- iph->id = htons(ip_id_count++);
- skb->nh.iph = iph;
-
- if (opt && opt->optlen) {
- iph->ihl += opt->optlen>>2;
- ip_options_build(skb, opt, daddr, rt, 0);
- }
-
- dev = rt->u.dst.dev;
-
-#ifdef CONFIG_FIREWALL
- /* Now we have no better mechanism to notify about error. */
- switch (call_out_firewall(PF_INET, dev, iph, NULL, &skb)) {
- case FW_REJECT:
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
- /* Fall thru... */
- case FW_BLOCK:
- case FW_QUEUE:
- kfree_skb(skb);
- return;
- }
-#endif
-
- ip_send_check(iph);
-
- /* Send it out. */
- skb->dst->output(skb);
- return;
-}
-
-int __ip_finish_output(struct sk_buff *skb)
-{
- return ip_finish_output(skb);
-}
-
-int ip_mc_output(struct sk_buff *skb)
-{
- struct sock *sk = skb->sk;
- struct rtable *rt = (struct rtable*)skb->dst;
- struct device *dev = rt->u.dst.dev;
-
- /*
- * If the indicated interface is up and running, send the packet.
- */
-
- ip_statistics.IpOutRequests++;
-#ifdef CONFIG_IP_ROUTE_NAT
- if (rt->rt_flags & RTCF_NAT)
- ip_do_nat(skb);
-#endif
-
- skb->dev = dev;
- skb->protocol = __constant_htons(ETH_P_IP);
-
- /*
- * Multicasts are looped back for other local users
- */
-
- if (rt->rt_flags&RTCF_MULTICAST && (!sk || sk->ip_mc_loop)) {
-#ifdef CONFIG_IP_MROUTE
- /* Small optimization: do not loopback not local frames,
- which returned after forwarding; they will be dropped
- by ip_mr_input in any case.
- Note, that local frames are looped back to be delivered
- to local recipients.
-
- This check is duplicated in ip_mr_input at the moment.
- */
- if ((rt->rt_flags&RTCF_LOCAL) || !(IPCB(skb)->flags&IPSKB_FORWARDED))
-#endif
- dev_loopback_xmit(skb);
-
- /* Multicasts with ttl 0 must not go beyond the host */
-
- if (skb->nh.iph->ttl == 0) {
- kfree_skb(skb);
- return 0;
- }
- }
-
- if (rt->rt_flags&RTCF_BROADCAST)
- dev_loopback_xmit(skb);
-
- return ip_finish_output(skb);
-}
-
-int ip_output(struct sk_buff *skb)
-{
- char *str1 = "pfinet ip_output check point 1\n";
- char *str2 = "pfinet ip_output check point 2\n";
- int stderr_fd = fileno (stderr);
- int ret;
-
-#ifdef CONFIG_IP_ROUTE_NAT
- struct rtable *rt = (struct rtable*)skb->dst;
-#endif
-
- ip_statistics.IpOutRequests++;
-
-#ifdef CONFIG_IP_ROUTE_NAT
- if (rt->rt_flags&RTCF_NAT)
- ip_do_nat(skb);
-#endif
-
- write (stderr_fd, str1, strlen (str1) + 1);
- fflush (stderr);
- ret = ip_finish_output(skb);
- return ret;
-}
-
-/* Queues a packet to be sent, and starts the transmitter if necessary.
- * This routine also needs to put in the total length and compute the
- * checksum. We use to do this in two stages, ip_build_header() then
- * this, but that scheme created a mess when routes disappeared etc.
- * So we do it all here, and the TCP send engine has been changed to
- * match. (No more unroutable FIN disasters, etc. wheee...) This will
- * most likely make other reliable transport layers above IP easier
- * to implement under Linux.
- */
-void ip_queue_xmit(struct sk_buff *skb)
-{
- struct sock *sk = skb->sk;
- struct ip_options *opt = sk->opt;
- struct rtable *rt;
- struct device *dev;
- struct iphdr *iph;
- unsigned int tot_len;
-
- /* Make sure we can route this packet. */
- rt = (struct rtable *) sk->dst_cache;
- if(rt == NULL || rt->u.dst.obsolete) {
- u32 daddr;
-
- sk->dst_cache = NULL;
- ip_rt_put(rt);
-
- /* Use correct destination address if we have options. */
- daddr = sk->daddr;
- if(opt && opt->srr)
- daddr = opt->faddr;
-
- /* If this fails, retransmit mechanism of transport layer will
- * keep trying until route appears or the connection times itself
- * out.
- */
- if(ip_route_output(&rt, daddr, sk->saddr,
- RT_TOS(sk->ip_tos) | RTO_CONN | sk->localroute,
- sk->bound_dev_if))
- goto drop;
- sk->dst_cache = &rt->u.dst;
- }
- if(opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway)
- goto no_route;
-
- /* We have a route, so grab a reference. */
- skb->dst = dst_clone(sk->dst_cache);
-
- /* OK, we know where to send it, allocate and build IP header. */
- iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));
- iph->version = 4;
- iph->ihl = 5;
- iph->tos = sk->ip_tos;
- iph->frag_off = 0;
- iph->ttl = sk->ip_ttl;
- iph->daddr = rt->rt_dst;
- iph->saddr = rt->rt_src;
- iph->protocol = sk->protocol;
- skb->nh.iph = iph;
- /* Transport layer set skb->h.foo itself. */
-
- if(opt && opt->optlen) {
- iph->ihl += opt->optlen >> 2;
- ip_options_build(skb, opt, sk->daddr, rt, 0);
- }
-
- tot_len = skb->len;
- iph->tot_len = htons(tot_len);
- iph->id = htons(ip_id_count++);
-
- dev = rt->u.dst.dev;
-
-#ifdef CONFIG_FIREWALL
- /* Now we have no better mechanism to notify about error. */
- switch (call_out_firewall(PF_INET, dev, iph, NULL, &skb)) {
- case FW_REJECT:
- start_bh_atomic();
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
- end_bh_atomic();
- /* Fall thru... */
- case FW_BLOCK:
- case FW_QUEUE:
- goto drop;
- }
-#endif
-
- /* This can happen when the transport layer has segments queued
- * with a cached route, and by the time we get here things are
- * re-routed to a device with a different MTU than the original
- * device. Sick, but we must cover it.
- */
- if (skb_headroom(skb) < dev->hard_header_len && dev->hard_header) {
- struct sk_buff *skb2;
-
- skb2 = skb_realloc_headroom(skb, (dev->hard_header_len + 15) & ~15);
- kfree_skb(skb);
- if (skb2 == NULL)
- return;
- if (sk)
- skb_set_owner_w(skb2, sk);
- skb = skb2;
- iph = skb->nh.iph;
- }
-
- /* Do we need to fragment. Again this is inefficient. We
- * need to somehow lock the original buffer and use bits of it.
- */
- if (tot_len > rt->u.dst.pmtu)
- goto fragment;
-
- if (ip_dont_fragment(sk, &rt->u.dst))
- iph->frag_off |= __constant_htons(IP_DF);
-
- /* Add an IP checksum. */
- ip_send_check(iph);
-
- skb->priority = sk->priority;
- skb->dst->output(skb);
- return;
-
-fragment:
- if (ip_dont_fragment(sk, &rt->u.dst) &&
- tot_len > (iph->ihl<<2) + sizeof(struct tcphdr)+16) {
- /* Reject packet ONLY if TCP might fragment
- it itself, if were careful enough.
- Test is not precise (f.e. it does not take sacks
- into account). Actually, tcp should make it. --ANK (980801)
- */
- iph->frag_off |= __constant_htons(IP_DF);
- NETDEBUG(printk(KERN_DEBUG "sending pkt_too_big to self\n"));
-
- /* icmp_send is not reenterable, so that bh_atomic... --ANK */
- start_bh_atomic();
- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
- htonl(rt->u.dst.pmtu));
- end_bh_atomic();
- goto drop;
- }
- ip_fragment(skb, skb->dst->output);
- return;
-
-no_route:
- sk->dst_cache = NULL;
- ip_rt_put(rt);
- ip_statistics.IpOutNoRoutes++;
- /* Fall through... */
-drop:
- kfree_skb(skb);
-}
-
-/*
- * Build and send a packet, with as little as one copy
- *
- * Doesn't care much about ip options... option length can be
- * different for fragment at 0 and other fragments.
- *
- * Note that the fragment at the highest offset is sent first,
- * so the getfrag routine can fill in the TCP/UDP checksum header
- * field in the last fragment it sends... actually it also helps
- * the reassemblers, they can put most packets in at the head of
- * the fragment queue, and they know the total size in advance. This
- * last feature will measurably improve the Linux fragment handler one
- * day.
- *
- * The callback has five args, an arbitrary pointer (copy of frag),
- * the source IP address (may depend on the routing table), the
- * destination address (char *), the offset to copy from, and the
- * length to be copied.
- */
-
-int ip_build_xmit_slow(struct sock *sk,
- int getfrag (const void *,
- char *,
- unsigned int,
- unsigned int),
- const void *frag,
- unsigned length,
- struct ipcm_cookie *ipc,
- struct rtable *rt,
- int flags)
-{
- unsigned int fraglen, maxfraglen, fragheaderlen;
- int err;
- int offset, mf;
- int mtu;
- unsigned short id;
-
- int hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
- int nfrags=0;
- struct ip_options *opt = ipc->opt;
- int df = 0;
-
- mtu = rt->u.dst.pmtu;
- if (ip_dont_fragment(sk, &rt->u.dst))
- df = htons(IP_DF);
-
- length -= sizeof(struct iphdr);
-
- if (opt) {
- fragheaderlen = sizeof(struct iphdr) + opt->optlen;
- maxfraglen = ((mtu-sizeof(struct iphdr)-opt->optlen) & ~7) + fragheaderlen;
- } else {
- fragheaderlen = sizeof(struct iphdr);
-
- /*
- * Fragheaderlen is the size of 'overhead' on each buffer. Now work
- * out the size of the frames to send.
- */
-
- maxfraglen = ((mtu-sizeof(struct iphdr)) & ~7) + fragheaderlen;
- }
-
- if (length + fragheaderlen > 0xFFFF) {
- ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, mtu);
- return -EMSGSIZE;
- }
-
- /*
- * Start at the end of the frame by handling the remainder.
- */
-
- offset = length - (length % (maxfraglen - fragheaderlen));
-
- /*
- * Amount of memory to allocate for final fragment.
- */
-
- fraglen = length - offset + fragheaderlen;
-
- if (length-offset==0) {
- fraglen = maxfraglen;
- offset -= maxfraglen-fragheaderlen;
- }
-
-
- /*
- * The last fragment will not have MF (more fragments) set.
- */
-
- mf = 0;
-
- /*
- * Don't fragment packets for path mtu discovery.
- */
-
- if (offset > 0 && df) {
- ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, mtu);
- return(-EMSGSIZE);
- }
-
- /*
- * Lock the device lists.
- */
-
- dev_lock_list();
-
- /*
- * Get an identifier
- */
-
- id = htons(ip_id_count++);
-
- /*
- * Begin outputting the bytes.
- */
-
- do {
- char *data;
- struct sk_buff * skb;
-
- /*
- * Get the memory we require with some space left for alignment.
- */
-
- skb = sock_alloc_send_skb(sk, fraglen+hh_len+15, 0, flags&MSG_DONTWAIT, &err);
- if (skb == NULL)
- goto error;
-
- /*
- * Fill in the control structures
- */
-
- skb->priority = sk->priority;
- skb->dst = dst_clone(&rt->u.dst);
- skb_reserve(skb, hh_len);
-
- /*
- * Find where to start putting bytes.
- */
-
- data = skb_put(skb, fraglen);
- skb->nh.iph = (struct iphdr *)data;
-
- /*
- * Only write IP header onto non-raw packets
- */
-
- {
- struct iphdr *iph = (struct iphdr *)data;
-
- iph->version = 4;
- iph->ihl = 5;
- if (opt) {
- iph->ihl += opt->optlen>>2;
- ip_options_build(skb, opt,
- ipc->addr, rt, offset);
- }
- iph->tos = sk->ip_tos;
- iph->tot_len = htons(fraglen - fragheaderlen + iph->ihl*4);
- iph->id = id;
- iph->frag_off = htons(offset>>3);
- iph->frag_off |= mf|df;
- if (rt->rt_type == RTN_MULTICAST)
- iph->ttl = sk->ip_mc_ttl;
- else
- iph->ttl = sk->ip_ttl;
- iph->protocol = sk->protocol;
- iph->check = 0;
- iph->saddr = rt->rt_src;
- iph->daddr = rt->rt_dst;
- iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
- data += iph->ihl*4;
-
- /*
- * Any further fragments will have MF set.
- */
-
- mf = htons(IP_MF);
- }
-
- /*
- * User data callback
- */
-
- if (getfrag(frag, data, offset, fraglen-fragheaderlen)) {
- err = -EFAULT;
- kfree_skb(skb);
- goto error;
- }
-
- offset -= (maxfraglen-fragheaderlen);
- fraglen = maxfraglen;
-
- nfrags++;
-
-#ifdef CONFIG_FIREWALL
- switch (call_out_firewall(PF_INET, rt->u.dst.dev, skb->nh.iph, NULL, &skb)) {
- case FW_QUEUE:
- kfree_skb(skb);
- continue;
- case FW_BLOCK:
- case FW_REJECT:
- kfree_skb(skb);
- err = -EPERM;
- goto error;
- }
-#endif
-
- err = -ENETDOWN;
- if (rt->u.dst.output(skb))
- goto error;
- } while (offset >= 0);
-
- if (nfrags>1)
- ip_statistics.IpFragCreates += nfrags;
- dev_unlock_list();
- return 0;
-
-error:
- ip_statistics.IpOutDiscards++;
- if (nfrags>1)
- ip_statistics.IpFragCreates += nfrags;
- dev_unlock_list();
- return err;
-}
-
-
-/*
- * Fast path for unfragmented packets.
- */
-int ip_build_xmit(struct sock *sk,
- int getfrag (const void *,
- char *,
- unsigned int,
- unsigned int),
- const void *frag,
- unsigned length,
- struct ipcm_cookie *ipc,
- struct rtable *rt,
- int flags)
-{
- int err;
- struct sk_buff *skb;
- int df;
- struct iphdr *iph;
-
- /*
- * Try the simple case first. This leaves fragmented frames, and by
- * choice RAW frames within 20 bytes of maximum size(rare) to the long path
- */
-
- if (!sk->ip_hdrincl) {
- length += sizeof(struct iphdr);
-
- /*
- * Check for slow path.
- */
- if (length > rt->u.dst.pmtu || ipc->opt != NULL)
- return ip_build_xmit_slow(sk,getfrag,frag,length,ipc,rt,flags);
- } else {
- if (length > rt->u.dst.dev->mtu) {
- ip_local_error(sk, EMSGSIZE, rt->rt_dst, sk->dport, rt->u.dst.dev->mtu);
- return -EMSGSIZE;
- }
- }
-
- /*
- * Do path mtu discovery if needed.
- */
- df = 0;
- if (ip_dont_fragment(sk, &rt->u.dst))
- df = htons(IP_DF);
-
- /*
- * Fast path for unfragmented frames without options.
- */
- {
- int hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
-
- skb = sock_alloc_send_skb(sk, length+hh_len+15,
- 0, flags&MSG_DONTWAIT, &err);
- if(skb==NULL)
- goto error;
- skb_reserve(skb, hh_len);
- }
-
- skb->priority = sk->priority;
- skb->dst = dst_clone(&rt->u.dst);
-
- skb->nh.iph = iph = (struct iphdr *)skb_put(skb, length);
-
- dev_lock_list();
-
- if(!sk->ip_hdrincl) {
- iph->version=4;
- iph->ihl=5;
- iph->tos=sk->ip_tos;
- iph->tot_len = htons(length);
- iph->id=htons(ip_id_count++);
- iph->frag_off = df;
- iph->ttl=sk->ip_mc_ttl;
- if (rt->rt_type != RTN_MULTICAST)
- iph->ttl=sk->ip_ttl;
- iph->protocol=sk->protocol;
- iph->saddr=rt->rt_src;
- iph->daddr=rt->rt_dst;
- iph->check=0;
- iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
- err = getfrag(frag, ((char *)iph)+iph->ihl*4,0, length-iph->ihl*4);
- }
- else
- err = getfrag(frag, (void *)iph, 0, length);
-
- dev_unlock_list();
-
- if (err)
- goto error_fault;
-
-#ifdef CONFIG_FIREWALL
- switch (call_out_firewall(PF_INET, rt->u.dst.dev, iph, NULL, &skb)) {
- case FW_QUEUE:
- kfree_skb(skb);
- return 0;
- case FW_BLOCK:
- case FW_REJECT:
- kfree_skb(skb);
- err = -EPERM;
- goto error;
- }
-#endif
-
- return rt->u.dst.output(skb);
-
-error_fault:
- err = -EFAULT;
- kfree_skb(skb);
-error:
- ip_statistics.IpOutDiscards++;
- return err;
-}
-
-
-
-/*
- * This IP datagram is too large to be sent in one piece. Break it up into
- * smaller pieces (each of size equal to IP header plus
- * a block of the data of the original IP data part) that will yet fit in a
- * single device frame, and queue such a frame for sending.
- *
- * Yes this is inefficient, feel free to submit a quicker one.
- */
-
-void ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
-{
- struct iphdr *iph;
- unsigned char *raw;
- unsigned char *ptr;
- struct device *dev;
- struct sk_buff *skb2;
- unsigned int mtu, hlen, left, len;
- int offset;
- int not_last_frag;
- struct rtable *rt = (struct rtable*)skb->dst;
-
- dev = rt->u.dst.dev;
-
- /*
- * Point into the IP datagram header.
- */
-
- raw = skb->nh.raw;
- iph = (struct iphdr*)raw;
-
- /*
- * Setup starting values.
- */
-
- hlen = iph->ihl * 4;
- left = ntohs(iph->tot_len) - hlen; /* Space per frame */
- mtu = rt->u.dst.pmtu - hlen; /* Size of data space */
- ptr = raw + hlen; /* Where to start from */
-
- /*
- * The protocol doesn't seem to say what to do in the case that the
- * frame + options doesn't fit the mtu. As it used to fall down dead
- * in this case we were fortunate it didn't happen
- *
- * It is impossible, because mtu>=68. --ANK (980801)
- */
-
-#ifdef CONFIG_NET_PARANOIA
- if (mtu<8)
- goto fail;
-#endif
-
- /*
- * Fragment the datagram.
- */
-
- offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;
- not_last_frag = iph->frag_off & htons(IP_MF);
-
- /*
- * Keep copying data until we run out.
- */
-
- while(left > 0) {
- len = left;
- /* IF: it doesn't fit, use 'mtu' - the data space left */
- if (len > mtu)
- len = mtu;
- /* IF: we are not sending upto and including the packet end
- then align the next start on an eight byte boundary */
- if (len < left) {
- len &= ~7;
- }
- /*
- * Allocate buffer.
- */
-
- if ((skb2 = alloc_skb(len+hlen+dev->hard_header_len+15,GFP_ATOMIC)) == NULL) {
- NETDEBUG(printk(KERN_INFO "IP: frag: no memory for new fragment!\n"));
- goto fail;
- }
-
- /*
- * Set up data on packet
- */
-
- skb2->pkt_type = skb->pkt_type;
- skb2->priority = skb->priority;
- skb_reserve(skb2, (dev->hard_header_len+15)&~15);
- skb_put(skb2, len + hlen);
- skb2->nh.raw = skb2->data;
- skb2->h.raw = skb2->data + hlen;
-
- /*
- * Charge the memory for the fragment to any owner
- * it might possess
- */
-
- if (skb->sk)
- skb_set_owner_w(skb2, skb->sk);
- skb2->dst = dst_clone(skb->dst);
-
- /*
- * Copy the packet header into the new buffer.
- */
-
- memcpy(skb2->nh.raw, raw, hlen);
-
- /*
- * Copy a block of the IP datagram.
- */
- memcpy(skb2->h.raw, ptr, len);
- left -= len;
-
- /*
- * Fill in the new header fields.
- */
- iph = skb2->nh.iph;
- iph->frag_off = htons((offset >> 3));
-
- /* ANK: dirty, but effective trick. Upgrade options only if
- * the segment to be fragmented was THE FIRST (otherwise,
- * options are already fixed) and make it ONCE
- * on the initial skb, so that all the following fragments
- * will inherit fixed options.
- */
- if (offset == 0)
- ip_options_fragment(skb);
-
- /*
- * Added AC : If we are fragmenting a fragment that's not the
- * last fragment then keep MF on each bit
- */
- if (left > 0 || not_last_frag)
- iph->frag_off |= htons(IP_MF);
- ptr += len;
- offset += len;
-
- /*
- * Put this fragment into the sending queue.
- */
-
- ip_statistics.IpFragCreates++;
-
- iph->tot_len = htons(len + hlen);
-
- ip_send_check(iph);
-
- output(skb2);
- }
- kfree_skb(skb);
- ip_statistics.IpFragOKs++;
- return;
-
-fail:
- kfree_skb(skb);
- ip_statistics.IpFragFails++;
-}
-
-/*
- * Fetch data from kernel space and fill in checksum if needed.
- */
-static int ip_reply_glue_bits(const void *dptr, char *to, unsigned int offset,
- unsigned int fraglen)
-{
- struct ip_reply_arg *dp = (struct ip_reply_arg*)dptr;
- u16 *pktp = (u16 *)to;
- struct iovec *iov;
- int len;
- int hdrflag = 1;
-
- iov = &dp->iov[0];
- if (offset >= iov->iov_len) {
- offset -= iov->iov_len;
- iov++;
- hdrflag = 0;
- }
- len = iov->iov_len - offset;
- if (fraglen > len) { /* overlapping. */
- dp->csum = csum_partial_copy_nocheck(iov->iov_base+offset, to, len,
- dp->csum);
- offset = 0;
- fraglen -= len;
- to += len;
- iov++;
- }
-
- dp->csum = csum_partial_copy_nocheck(iov->iov_base+offset, to, fraglen,
- dp->csum);
-
- if (hdrflag && dp->csumoffset)
- *(pktp + dp->csumoffset) = csum_fold(dp->csum); /* fill in checksum */
- return 0;
-}
-
-/*
- * Generic function to send a packet as reply to another packet.
- * Used to send TCP resets so far. ICMP should use this function too.
- *
- * Should run single threaded per socket because it uses the sock
- * structure to pass arguments.
- */
-void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *arg,
- unsigned int len)
-{
- struct {
- struct ip_options opt;
- char data[40];
- } replyopts;
- struct ipcm_cookie ipc;
- u32 daddr;
- struct rtable *rt = (struct rtable*)skb->dst;
-
- if (ip_options_echo(&replyopts.opt, skb))
- return;
-
- sk->ip_tos = skb->nh.iph->tos;
- sk->priority = skb->priority;
- sk->protocol = skb->nh.iph->protocol;
-
- daddr = ipc.addr = rt->rt_src;
- ipc.opt = &replyopts.opt;
-
- if (ipc.opt->srr)
- daddr = replyopts.opt.faddr;
- if (ip_route_output(&rt, daddr, rt->rt_spec_dst, RT_TOS(skb->nh.iph->tos), 0))
- return;
-
- /* And let IP do all the hard work. */
- ip_build_xmit(sk, ip_reply_glue_bits, arg, len, &ipc, rt, MSG_DONTWAIT);
- ip_rt_put(rt);
-}
-
-/*
- * IP protocol layer initialiser
- */
-
-static struct packet_type ip_packet_type =
-{
- __constant_htons(ETH_P_IP),
- NULL, /* All devices */
- ip_rcv,
- NULL,
- NULL,
-};
-
-
-
-#ifdef CONFIG_PROC_FS
-#ifdef CONFIG_IP_MULTICAST
-static struct proc_dir_entry proc_net_igmp = {
- PROC_NET_IGMP, 4, "igmp",
- S_IFREG | S_IRUGO, 1, 0, 0,
- 0, &proc_net_inode_operations,
- ip_mc_procinfo
-};
-#endif
-#endif
-
-/*
- * IP registers the packet type and then calls the subprotocol initialisers
- */
-
-__initfunc(void ip_init(void))
-{
- dev_add_pack(&ip_packet_type);
-
- ip_rt_init();
-
-#ifdef CONFIG_PROC_FS
-#ifdef CONFIG_IP_MULTICAST
- proc_net_register(&proc_net_igmp);
-#endif
-#endif
-}
-