Newer
Older
*
* Based on linux/net/ipv4/ip_output.c
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*
* Changes:
* A.N.Kuznetsov : airthmetics in fragmentation.
* extension headers are implemented.
* route changes now work.
* ip6_forward does not confuse sniffers.
* etc.
*
* H. von Brand : Added missing #include <linux/string.h>
* Imran Patel : frag id should be in NBO
* Kazunori MIYAZAWA @USAGI
* : add ip6_append_data and related functions
* for datagram xmit
*/
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/in6.h>
#include <linux/tcp.h>
#include <linux/route.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv6.h>
#include <net/sock.h>
#include <net/snmp.h>
#include <net/ipv6.h>
#include <net/ndisc.h>
#include <net/protocol.h>
#include <net/ip6_route.h>
#include <net/addrconf.h>
#include <net/rawv6.h>
#include <net/icmp.h>
#include <net/xfrm.h>
#include <net/checksum.h>
#include <linux/mroute6.h>
static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *));
static __inline__ void ipv6_select_ident(struct sk_buff *skb, struct frag_hdr *fhdr)
{
static u32 ipv6_fragmentation_id = 1;
static DEFINE_SPINLOCK(ip6_id_lock);
spin_lock_bh(&ip6_id_lock);
fhdr->identification = htonl(ipv6_fragmentation_id);
if (++ipv6_fragmentation_id == 0)
ipv6_fragmentation_id = 1;
spin_unlock_bh(&ip6_id_lock);
}
int __ip6_local_out(struct sk_buff *skb)
{
int len;
len = skb->len - sizeof(struct ipv6hdr);
if (len > IPV6_MAXPLEN)
len = 0;
ipv6_hdr(skb)->payload_len = htons(len);
return nf_hook(PF_INET6, NF_INET_LOCAL_OUT, skb, NULL, skb->dst->dev,
dst_output);
}
int ip6_local_out(struct sk_buff *skb)
{
int err;
err = __ip6_local_out(skb);
if (likely(err == 1))
err = dst_output(skb);
return err;
}
EXPORT_SYMBOL_GPL(ip6_local_out);
static int ip6_output_finish(struct sk_buff *skb)
if (dst->hh)
return neigh_hh_output(dst->hh, skb);
else if (dst->neighbour)
IP6_INC_STATS_BH(ip6_dst_idev(dst), IPSTATS_MIB_OUTNOROUTES);
kfree_skb(skb);
return -EINVAL;
}
/* dev_loopback_xmit for use with netfilter. */
static int ip6_dev_loopback_xmit(struct sk_buff *newskb)
{
skb_reset_mac_header(newskb);
__skb_pull(newskb, skb_network_offset(newskb));
newskb->pkt_type = PACKET_LOOPBACK;
newskb->ip_summed = CHECKSUM_UNNECESSARY;
BUG_TRAP(newskb->dst);
netif_rx(newskb);
return 0;
}
static int ip6_output2(struct sk_buff *skb)
{
struct dst_entry *dst = skb->dst;
struct net_device *dev = dst->dev;
skb->protocol = htons(ETH_P_IPV6);
skb->dev = dev;
if (ipv6_addr_is_multicast(&ipv6_hdr(skb)->daddr)) {
struct ipv6_pinfo* np = skb->sk ? inet6_sk(skb->sk) : NULL;
struct inet6_dev *idev = ip6_dst_idev(skb->dst);
if (!(dev->flags & IFF_LOOPBACK) && (!np || np->mc_loop) &&
((mroute6_socket && !(IP6CB(skb)->flags & IP6SKB_FORWARDED)) ||
ipv6_chk_mcast_addr(dev, &ipv6_hdr(skb)->daddr,
&ipv6_hdr(skb)->saddr))) {
struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
/* Do not check for IFF_ALLMULTI; multicast routing
is not supported in any case.
*/
if (newskb)
NF_HOOK(PF_INET6, NF_INET_POST_ROUTING, newskb,
NULL, newskb->dev,
if (ipv6_hdr(skb)->hop_limit == 0) {
IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS);
IP6_INC_STATS(idev, IPSTATS_MIB_OUTMCASTPKTS);
return NF_HOOK(PF_INET6, NF_INET_POST_ROUTING, skb, NULL, skb->dev,
ip6_output_finish);
static inline int ip6_skb_dst_mtu(struct sk_buff *skb)
{
struct ipv6_pinfo *np = skb->sk ? inet6_sk(skb->sk) : NULL;
return (np && np->pmtudisc == IPV6_PMTUDISC_PROBE) ?
skb->dst->dev->mtu : dst_mtu(skb->dst);
}
if ((skb->len > ip6_skb_dst_mtu(skb) && !skb_is_gso(skb)) ||
return ip6_fragment(skb, ip6_output2);
else
return ip6_output2(skb);
}
/*
* xmit an sk_buff (used by TCP)
*/
int ip6_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
struct ipv6_txoptions *opt, int ipfragok)
{
struct ipv6_pinfo *np = inet6_sk(sk);
struct in6_addr *first_hop = &fl->fl6_dst;
struct dst_entry *dst = skb->dst;
struct ipv6hdr *hdr;
u8 proto = fl->proto;
int seg_len = skb->len;
int hlimit, tclass;
unsigned int head_room;
Loading
Loading full blame...