patch-2.1.79 linux/net/ipv6/ip6_output.c
Next file: linux/net/ipv6/ipv6_sockglue.c
Previous file: linux/net/ipv6/ip6_fw.c
Back to the patch index
Back to the overall index
- Lines: 371
- Date:
Mon Jan 12 15:28:28 1998
- Orig file:
v2.1.78/linux/net/ipv6/ip6_output.c
- Orig date:
Mon Dec 1 12:04:17 1997
diff -u --recursive --new-file v2.1.78/linux/net/ipv6/ip6_output.c linux/net/ipv6/ip6_output.c
@@ -5,7 +5,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
- * $Id: ip6_output.c,v 1.5 1997/09/21 18:33:14 kuznet Exp $
+ * $Id: ip6_output.c,v 1.7 1997/12/29 19:52:46 kuznet Exp $
*
* Based on linux/net/ipv4/ip_output.c
*
@@ -35,32 +35,33 @@
static u32 ipv6_fragmentation_id = 1;
-static void ipv6_build_mac_hdr(struct sk_buff *skb, struct dst_entry *dst,
- int len)
+int ip6_output(struct sk_buff *skb)
{
- struct device *dev;
-
-
- dev = dst->dev;
+ struct dst_entry *dst = skb->dst;
+ struct device *dev = dst->dev;
+ struct hh_cache *hh = dst->hh;
- skb->arp = 1;
-
- if (dev->hard_header) {
- int mac;
+ skb->protocol = __constant_htons(ETH_P_IPV6);
+ skb->dev = dev;
- /* Maybe when Alexey has done his new magic I'll hack this
- it seems to be worth 1-2% on IPv4 */
-#if 0
- if (dst->hh)
- hh_copy_header(dst->hh, skb);
+ if (hh) {
+#ifdef __alpha__
+ /* Alpha has disguisting memcpy. Help it. */
+ u64 *aligned_hdr = (u64*)(skb->data - 16);
+ u64 *aligned_hdr0 = hh->hh_data;
+ aligned_hdr[0] = aligned_hdr0[0];
+ aligned_hdr[1] = aligned_hdr0[1];
+#else
+ memcpy(skb->data - 16, hh->hh_data, 16);
#endif
- mac = dev->hard_header(skb, dev, ETH_P_IPV6, NULL, NULL, len);
-
- if (mac < 0)
- skb->arp = 0;
- }
-
- skb->mac.raw = skb->data;
+ skb_push(skb, dev->hard_header_len);
+ return hh->hh_output(skb);
+ } else if (dst->neighbour)
+ return dst->neighbour->output(skb);
+
+ printk(KERN_DEBUG "khm\n");
+ kfree_skb(skb, FREE_WRITE);
+ return -EINVAL;
}
/*
@@ -78,14 +79,15 @@
hdr = skb->nh.ipv6h;
- if (sk)
+ if (sk) {
np = &sk->net_pinfo.af_inet6;
- if (np && np->dst) {
- /*
- * dst_check returns NULL if route is no longer valid
- */
- dst = dst_check(&dst, np->dst_cookie);
+ if (sk->dst_cache) {
+ /*
+ * dst_check returns NULL if route is no longer valid
+ */
+ dst = dst_check(&sk->dst_cache, np->dst_cookie);
+ }
}
if (dst == NULL) {
@@ -95,24 +97,15 @@
/*
* NETUNREACH usually
*/
+ dst_release(dst);
return dst->error;
}
}
skb->dst = dst_clone(dst);
- skb->dev = dst->dev;
seg_len = skb->tail - ((unsigned char *) hdr);
-
- /*
- * Link Layer headers
- */
-
- skb->protocol = __constant_htons(ETH_P_IPV6);
hdr = skb->nh.ipv6h;
- ipv6_build_mac_hdr(skb, dst, seg_len);
-
-
/*
* Fill in the IPv6 header
*/
@@ -135,9 +128,10 @@
ipv6_statistics.Ip6OutRequests++;
dst->output(skb);
- if (sk)
- ip6_dst_store(sk, dst);
- else
+ if (sk) {
+ if (sk->dst_cache == NULL)
+ ip6_dst_store(sk, dst);
+ } else
dst_release(dst);
return 0;
@@ -163,8 +157,6 @@
totlen = len + sizeof(struct ipv6hdr);
- skb->mac.raw = skb->data;
-
hdr = (struct ipv6hdr *) skb_put(skb, sizeof(struct ipv6hdr));
skb->nh.ipv6h = hdr;
@@ -211,7 +203,7 @@
static int ip6_frag_xmit(struct sock *sk, inet_getfrag_t getfrag,
const void *data, struct dst_entry *dst,
struct flowi *fl, struct ipv6_options *opt,
- int hlimit, int flags, unsigned short length)
+ int hlimit, int flags, unsigned length)
{
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
struct ipv6hdr *hdr;
@@ -245,8 +237,6 @@
payl_len += opt->opt_flen;
}
- nfrags = payl_len / ((dst->pmtu - unfrag_len) & ~0x7);
-
/*
* Length of fragmented part on every packet but
* the last must be an:
@@ -255,6 +245,8 @@
frag_len = (dst->pmtu - unfrag_len) & ~0x7;
+ nfrags = payl_len / frag_len;
+
/*
* We must send from end to start because of
* UDP/ICMP checksums. We do a funny trick:
@@ -281,18 +273,9 @@
return err;
last_skb->dst = dst_clone(dst);
- last_skb->dev = dst->dev;
- last_skb->protocol = htons(ETH_P_IPV6);
last_skb->when = jiffies;
- last_skb->arp = 0;
- /*
- * build the mac header...
- */
- if (dst->dev->hard_header_len) {
- skb_reserve(last_skb, (dst->dev->hard_header_len + 15) & ~15);
- ipv6_build_mac_hdr(last_skb, dst, unfrag_len + frag_len);
- }
+ skb_reserve(last_skb, (dst->dev->hard_header_len + 15) & ~15);
hdr = (struct ipv6hdr *) skb_put(last_skb, sizeof(struct ipv6hdr));
last_skb->nh.ipv6h = hdr;
@@ -335,7 +318,9 @@
struct frag_hdr *fhdr2;
+#if 0
printk(KERN_DEBUG "sending frag %d\n", nfrags);
+#endif
skb = skb_copy(last_skb, sk->allocation);
if (skb == NULL)
@@ -370,7 +355,9 @@
return -EFAULT;
}
+#if 0
printk(KERN_DEBUG "sending last frag \n");
+#endif
hdr->payload_len = htons(unfrag_len + last_len -
sizeof(struct ipv6hdr));
@@ -383,18 +370,6 @@
last_skb->tail += last_len;
last_skb->len += last_len;
- /*
- * toss the mac header out and rebuild it.
- * needed because of the different frame length.
- * ie: not needed for an ethernet.
- */
-
- if (dst->dev->type != ARPHRD_ETHER && last_len != frag_len) {
- skb_pull(last_skb, (unsigned char *)last_skb->nh.ipv6h -
- last_skb->data);
- ipv6_build_mac_hdr(last_skb, dst, unfrag_len + last_len);
- }
-
ipv6_statistics.Ip6OutRequests++;
dst->output(last_skb);
@@ -402,7 +377,7 @@
}
int ip6_build_xmit(struct sock *sk, inet_getfrag_t getfrag, const void *data,
- struct flowi *fl, unsigned short length,
+ struct flowi *fl, unsigned length,
struct ipv6_options *opt, int hlimit, int flags)
{
struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
@@ -419,8 +394,8 @@
dst = NULL;
- if (np->dst)
- dst = dst_check(&np->dst, np->dst_cookie);
+ if (sk->dst_cache)
+ dst = dst_check(&sk->dst_cache, np->dst_cookie);
if (dst == NULL)
dst = ip6_route_output(sk, fl);
@@ -456,6 +431,16 @@
pktlength += sizeof(struct ipv6hdr);
if (opt)
pktlength += opt->opt_flen + opt->opt_nflen;
+
+ /* Due to conservative check made by caller,
+ pktlength cannot overflow here.
+
+ When (and if) jumbo option will be implemented
+ we could try soemething sort of:
+
+ if (pktlength < length) return -EMSGSIZE;
+
+ */
}
if (pktlength <= dst->pmtu) {
@@ -475,15 +460,9 @@
dev = dst->dev;
skb->dst = dst_clone(dst);
- skb->dev = dev;
- skb->protocol = htons(ETH_P_IPV6);
skb->when = jiffies;
- skb->arp = 0;
- if (dev && dev->hard_header_len) {
- skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
- ipv6_build_mac_hdr(skb, dst, pktlength);
- }
+ skb_reserve(skb, (dev->hard_header_len + 15) & ~15);
hdr = (struct ipv6hdr *) skb->tail;
skb->nh.ipv6h = hdr;
@@ -516,9 +495,18 @@
} else {
if (sk->ip_hdrincl)
return -EMSGSIZE;
-
+
+ /* pktlength includes IPv6 header, not included
+ in IPv6 payload length.
+ FIXME are non-fragmentable options included
+ in packet after defragmentation? If not, we
+ should subtract opt_nflen also. --ANK
+ */
+ if (pktlength > 0xFFFF + sizeof(struct ipv6hdr))
+ return -EMSGSIZE;
+
err = ip6_frag_xmit(sk, getfrag, data, dst, fl, opt, hlimit,
- flags, pktlength);
+ flags, length);
}
/*
@@ -526,7 +514,7 @@
*/
out:
- if (np->dst)
+ if (sk->dst_cache)
ip6_dst_store(sk, dst);
else
dst_release(dst);
@@ -569,7 +557,7 @@
if (skb->dev == dst->dev && dst->neighbour) {
struct in6_addr *target = NULL;
struct rt6_info *rt;
- struct nd_neigh *ndn = (struct nd_neigh *) dst->neighbour;
+ struct neighbour *n = dst->neighbour;
/*
* incoming and outgoing devices are the same
@@ -578,7 +566,7 @@
rt = (struct rt6_info *) dst;
if ((rt->rt6i_flags & RTF_GATEWAY))
- target = &ndn->ndn_addr;
+ target = (struct in6_addr*)&n->primary_key;
else
target = &hdr->daddr;
@@ -593,41 +581,12 @@
return -EMSGSIZE;
}
- skb->dev = dst->dev;
-
- /*
- * Rebuild the mac header
- */
- if (skb_headroom(skb) < dst->dev->hard_header_len) {
- struct sk_buff *buff;
-
- buff = alloc_skb(dst->dev->hard_header_len + skb->len + 15,
- GFP_ATOMIC);
-
- if (buff == NULL) {
- kfree_skb(skb, FREE_WRITE);
- return -ENOMEM;
- }
-
- skb_reserve(buff, (dst->dev->hard_header_len + 15) & ~15);
-
- buff->protocol = __constant_htons(ETH_P_IPV6);
- buff->h.raw = skb_put(buff, size);
- buff->dst = dst_clone(dst);
- buff->dev = dst->dev;
-
- memcpy(buff->h.raw, hdr, size);
- buff->nh.ipv6h = (struct ipv6hdr *) buff->h.raw;
- kfree_skb(skb, FREE_READ);
- skb = buff;
- } else {
- skb_pull(skb, skb->nh.raw - skb->data);
+ if (skb_headroom(skb) < dst->dev->hard_header_len || skb_cloned(skb)) {
+ struct sk_buff *skb2;
+ skb2 = skb_realloc_headroom(skb, (dst->dev->hard_header_len + 15)&~15);
+ kfree_skb(skb, FREE_WRITE);
+ skb = skb2;
}
-
- ipv6_build_mac_hdr(skb, dst, size);
-
- if (dst->neighbour)
- ndisc_event_send(dst->neighbour, skb);
ipv6_statistics.Ip6ForwDatagrams++;
dst->output(skb);
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov