13541 lines
364 KiB
Diff
13541 lines
364 KiB
Diff
diff -urN linux-2.6.11/Documentation/networking/ip-sysctl.txt x1/Documentation/networking/ip-sysctl.txt
|
|
--- linux-2.6.11/Documentation/networking/ip-sysctl.txt 2005-03-02 08:38:07.000000000 +0100
|
|
+++ x1/Documentation/networking/ip-sysctl.txt 2004-11-04 14:07:37.000000000 +0100
|
|
@@ -661,7 +661,7 @@
|
|
TRUE: disable IPv4-mapped address feature
|
|
FALSE: enable IPv4-mapped address feature
|
|
|
|
- Default: FALSE (as specified in RFC2553bis)
|
|
+ Default: FALSE (as specified in RFC3493)
|
|
|
|
IPv6 Fragmentation:
|
|
|
|
diff -urN linux-2.6.11/include/linux/icmpv6.h x1/include/linux/icmpv6.h
|
|
--- linux-2.6.11/include/linux/icmpv6.h 2005-03-02 08:37:50.000000000 +0100
|
|
+++ x1/include/linux/icmpv6.h 2004-08-04 02:07:47.000000000 +0200
|
|
@@ -40,14 +40,18 @@
|
|
struct icmpv6_nd_ra {
|
|
__u8 hop_limit;
|
|
#if defined(__LITTLE_ENDIAN_BITFIELD)
|
|
- __u8 reserved:6,
|
|
+ __u8 reserved:3,
|
|
+ router_pref:2,
|
|
+ home_agent:1,
|
|
other:1,
|
|
managed:1;
|
|
|
|
#elif defined(__BIG_ENDIAN_BITFIELD)
|
|
__u8 managed:1,
|
|
other:1,
|
|
- reserved:6;
|
|
+ home_agent:1,
|
|
+ router_pref:2,
|
|
+ reserved:3;
|
|
#else
|
|
#error "Please fix <asm/byteorder.h>"
|
|
#endif
|
|
@@ -70,6 +74,8 @@
|
|
#define icmp6_addrconf_managed icmp6_dataun.u_nd_ra.managed
|
|
#define icmp6_addrconf_other icmp6_dataun.u_nd_ra.other
|
|
#define icmp6_rt_lifetime icmp6_dataun.u_nd_ra.rt_lifetime
|
|
+#define icmp6_home_agent icmp6_dataun.u_nd_ra.home_agent
|
|
+#define icmp6_router_pref icmp6_dataun.u_nd_ra.router_pref
|
|
};
|
|
|
|
|
|
diff -urN linux-2.6.11/include/linux/in6.h x1/include/linux/in6.h
|
|
--- linux-2.6.11/include/linux/in6.h 2005-03-02 08:38:12.000000000 +0100
|
|
+++ x1/include/linux/in6.h 2005-02-03 07:02:41.000000000 +0100
|
|
@@ -40,14 +40,14 @@
|
|
#define s6_addr32 in6_u.u6_addr32
|
|
};
|
|
|
|
-/* IPv6 Wildcard Address (::) and Loopback Address (::1) defined in RFC2553
|
|
+/* IPv6 Wildcard Address (::) and Loopback Address (::1) defined in RFC3493
|
|
* NOTE: Be aware the IN6ADDR_* constants and in6addr_* externals are defined
|
|
* in network byte order, not in host byte order as are the IPv4 equivalents
|
|
*/
|
|
#if 0
|
|
extern const struct in6_addr in6addr_any;
|
|
-#define IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }
|
|
#endif
|
|
+#define IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }
|
|
extern const struct in6_addr in6addr_loopback;
|
|
#define IN6ADDR_LOOPBACK_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }
|
|
|
|
@@ -56,7 +56,7 @@
|
|
__u16 sin6_port; /* Transport layer port # */
|
|
__u32 sin6_flowinfo; /* IPv6 flow information */
|
|
struct in6_addr sin6_addr; /* IPv6 address */
|
|
- __u32 sin6_scope_id; /* scope id (new in RFC2553) */
|
|
+ __u32 sin6_scope_id; /* scope id */
|
|
};
|
|
|
|
struct ipv6_mreq {
|
|
@@ -198,4 +198,7 @@
|
|
* MCAST_MSFILTER 48
|
|
*/
|
|
|
|
+/* Netfilter */
|
|
+#define IPV6_NF_ORIGINAL_DST 80
|
|
+
|
|
#endif
|
|
diff -urN linux-2.6.11/include/linux/ip.h x1/include/linux/ip.h
|
|
--- linux-2.6.11/include/linux/ip.h 2005-03-02 08:37:52.000000000 +0100
|
|
+++ x1/include/linux/ip.h 2005-02-11 17:24:31.000000000 +0100
|
|
@@ -152,6 +152,7 @@
|
|
};
|
|
|
|
#define IPCORK_OPT 1 /* ip-options has been held in ipcork.opt */
|
|
+#define IPCORK_ALLFRAG 2 /* IPv6: always fragment */
|
|
|
|
static inline struct inet_sock *inet_sk(const struct sock *sk)
|
|
{
|
|
diff -urN linux-2.6.11/include/linux/ipv6.h x1/include/linux/ipv6.h
|
|
--- linux-2.6.11/include/linux/ipv6.h 2005-03-02 08:38:13.000000000 +0100
|
|
+++ x1/include/linux/ipv6.h 2005-02-09 16:31:39.000000000 +0100
|
|
@@ -145,6 +145,9 @@
|
|
__s32 max_desync_factor;
|
|
#endif
|
|
__s32 max_addresses;
|
|
+#ifdef CONFIG_IPV6_MROUTE
|
|
+ __s32 mc_forwarding;
|
|
+#endif
|
|
void *sysctl;
|
|
};
|
|
|
|
@@ -167,6 +170,9 @@
|
|
DEVCONF_MAX_DESYNC_FACTOR,
|
|
DEVCONF_MAX_ADDRESSES,
|
|
DEVCONF_FORCE_MLD_VERSION,
|
|
+#ifdef CONFIG_IPV6_MROUTE
|
|
+ DEVCONF_MCFORWARDING,
|
|
+#endif
|
|
DEVCONF_MAX
|
|
};
|
|
|
|
diff -urN linux-2.6.11/include/linux/ipv6_route.h x1/include/linux/ipv6_route.h
|
|
--- linux-2.6.11/include/linux/ipv6_route.h 2005-03-02 08:37:50.000000000 +0100
|
|
+++ x1/include/linux/ipv6_route.h 2004-11-25 06:33:04.000000000 +0100
|
|
@@ -19,6 +19,12 @@
|
|
#define RTF_ADDRCONF 0x00040000 /* addrconf route - RA */
|
|
#define RTF_PREFIX_RT 0x00080000 /* A prefix only route - RA */
|
|
|
|
+#define RTF_PREF_HIGH 0x08000000
|
|
+#define RTF_PREF_LOW 0x18000000
|
|
+#define RTF_PREF_INVAL 0x10000000
|
|
+#define RTF_PREF_MASK 0x18000000
|
|
+#define RTF_PREF(pref) (((pref)&3)<<27)
|
|
+
|
|
#define RTF_NONEXTHOP 0x00200000 /* route with no nexthop */
|
|
#define RTF_EXPIRES 0x00400000
|
|
|
|
@@ -28,6 +34,11 @@
|
|
|
|
#define RTF_LOCAL 0x80000000
|
|
|
|
+#ifdef __KERNEL__
|
|
+#define IPV6_UNSHIFT_PREF(flag) (((flag)&RTF_PREF_MASK)>>27)
|
|
+#define IPV6_SIGNEDPREF(pref) ((((pref)+2)&3)-2)
|
|
+#endif
|
|
+
|
|
struct in6_rtmsg {
|
|
struct in6_addr rtmsg_dst;
|
|
struct in6_addr rtmsg_src;
|
|
diff -urN linux-2.6.11/include/linux/mroute6.h x1/include/linux/mroute6.h
|
|
--- linux-2.6.11/include/linux/mroute6.h 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/include/linux/mroute6.h 2005-02-09 16:31:39.000000000 +0100
|
|
@@ -0,0 +1,296 @@
|
|
+#ifndef __LINUX_MROUTE6_H
|
|
+#define __LINUX_MROUTE6_H
|
|
+
|
|
+#include <linux/sockios.h>
|
|
+
|
|
+/*
|
|
+ * Based on the MROUTING 3.5 defines primarily to keep
|
|
+ * source compatibility with BSD.
|
|
+ *
|
|
+ * See the pim6sd code for the original history.
|
|
+ *
|
|
+ * Protocol Independent Multicast (PIM) data structures included
|
|
+ * Carlos Picoto (cap@di.fc.ul.pt)
|
|
+ *
|
|
+ */
|
|
+
|
|
+#define MRT6_BASE 200
|
|
+#define MRT6_INIT (MRT6_BASE) /* Activate the kernel mroute code */
|
|
+#define MRT6_DONE (MRT6_BASE+1) /* Shutdown the kernel mroute */
|
|
+#define MRT6_ADD_MIF (MRT6_BASE+2) /* Add a virtual interface */
|
|
+#define MRT6_DEL_MIF (MRT6_BASE+3) /* Delete a virtual interface */
|
|
+#define MRT6_ADD_MFC (MRT6_BASE+4) /* Add a multicast forwarding entry */
|
|
+#define MRT6_DEL_MFC (MRT6_BASE+5) /* Delete a multicast forwarding entry */
|
|
+#define MRT6_VERSION (MRT6_BASE+6) /* Get the kernel multicast version */
|
|
+#define MRT6_ASSERT (MRT6_BASE+7) /* Activate PIM assert mode */
|
|
+#define MRT6_PIM (MRT6_BASE+8) /* enable PIM code */
|
|
+
|
|
+#define SIOCGETMIFCNT_IN6 SIOCPROTOPRIVATE /* IP protocol privates */
|
|
+#define SIOCGETSGCNT_IN6 (SIOCPROTOPRIVATE+1)
|
|
+#define SIOCGETRPF (SIOCPROTOPRIVATE+2)
|
|
+
|
|
+#define MAXMIFS 32
|
|
+typedef unsigned long mifbitmap_t; /* User mode code depends on this lot */
|
|
+typedef unsigned short mifi_t;
|
|
+#define ALL_MIFS ((mifi_t)(-1))
|
|
+
|
|
+#ifndef IF_SETSIZE
|
|
+#define IF_SETSIZE 256
|
|
+#endif
|
|
+
|
|
+typedef u_int32_t if_mask;
|
|
+#define NIFBITS (sizeof(if_mask) * 8) /* bits per mask */
|
|
+
|
|
+#ifndef howmany
|
|
+#define howmany(x, y) (((x) + ((y) - 1)) / (y))
|
|
+#endif
|
|
+
|
|
+typedef struct if_set {
|
|
+ if_mask ifs_bits[howmany(IF_SETSIZE, NIFBITS)];
|
|
+} if_set;
|
|
+
|
|
+#define IF_SET(n, p) ((p)->ifs_bits[(n)/NIFBITS] |= (1 << ((n) % NIFBITS)))
|
|
+#define IF_CLR(n, p) ((p)->ifs_bits[(n)/NIFBITS] &= ~(1 << ((n) % NIFBITS)))
|
|
+#define IF_ISSET(n, p) ((p)->ifs_bits[(n)/NIFBITS] & (1 << ((n) % NIFBITS)))
|
|
+#define IF_COPY(f, t) bcopy(f, t, sizeof(*(f)))
|
|
+#define IF_ZERO(p) bzero(p, sizeof(*(p)))
|
|
+
|
|
+/*
|
|
+ Same idea as select
|
|
+
|
|
+#define VIFM_SET(n,m) ((m)|=(1<<(n)))
|
|
+#define VIFM_CLR(n,m) ((m)&=~(1<<(n)))
|
|
+#define VIFM_ISSET(n,m) ((m)&(1<<(n)))
|
|
+#define VIFM_CLRALL(m) ((m)=0)
|
|
+#define VIFM_COPY(mfrom,mto) ((mto)=(mfrom))
|
|
+#define VIFM_SAME(m1,m2) ((m1)==(m2))
|
|
+*/
|
|
+
|
|
+/*
|
|
+ * Passed by mrouted for an MRT_ADD_MIF - again we use the
|
|
+ * mrouted 3.6 structures for compatibility
|
|
+ */
|
|
+
|
|
+struct mif6ctl {
|
|
+ mifi_t mif6c_mifi; /* Index of MIF */
|
|
+ unsigned char mif6c_flags; /* MIFF_ flags */
|
|
+ unsigned char vifc_threshold; /* ttl limit */
|
|
+ unsigned int vifc_rate_limit; /* Rate limiter values (NI) */
|
|
+ u_short mif6c_pifi; /* the index of the physical IF */
|
|
+};
|
|
+
|
|
+#define MIFF_REGISTER 0x1 /* register vif */
|
|
+
|
|
+/*
|
|
+ * Cache manipulation structures for mrouted and PIMd
|
|
+ */
|
|
+
|
|
+struct mf6cctl
|
|
+{
|
|
+ struct sockaddr_in6 mf6cc_origin; /* Origin of mcast */
|
|
+ struct sockaddr_in6 mf6cc_mcastgrp; /* Group in question */
|
|
+ mifi_t mf6cc_parent; /* Where it arrived */
|
|
+ struct if_set mf6cc_ifset; /* Where it is going */
|
|
+ unsigned int mfcc_pkt_cnt; /* pkt count for src-grp */
|
|
+ unsigned int mfcc_byte_cnt;
|
|
+ unsigned int mfcc_wrong_if;
|
|
+ int mfcc_expire;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Group count retrieval for pim6sd
|
|
+ */
|
|
+
|
|
+struct sioc_sg_req6
|
|
+{
|
|
+ struct sockaddr_in6 src;
|
|
+ struct sockaddr_in6 grp;
|
|
+ unsigned long pktcnt;
|
|
+ unsigned long bytecnt;
|
|
+ unsigned long wrong_if;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * To get vif packet counts
|
|
+ */
|
|
+
|
|
+struct sioc_mif_req6
|
|
+{
|
|
+ mifi_t mifi; /* Which iface */
|
|
+ unsigned long icount; /* In packets */
|
|
+ unsigned long ocount; /* Out packets */
|
|
+ unsigned long ibytes; /* In bytes */
|
|
+ unsigned long obytes; /* Out bytes */
|
|
+};
|
|
+
|
|
+/*
|
|
+ * That's all usermode folks
|
|
+ */
|
|
+
|
|
+#ifdef __KERNEL__
|
|
+struct inet6_dev * ipv6_find_idev(struct net_device *dev);
|
|
+#include <net/sock.h>
|
|
+
|
|
+extern int ip6_mroute_setsockopt(struct sock *, int, char __user *, int);
|
|
+extern int ip6_mroute_getsockopt(struct sock *, int, char __user *, int __user *);
|
|
+extern int ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg);
|
|
+extern void ip6_mr_init(void);
|
|
+
|
|
+struct mif_device
|
|
+{
|
|
+ struct net_device *dev; /* Device we are using */
|
|
+ unsigned long bytes_in,bytes_out;
|
|
+ unsigned long pkt_in,pkt_out; /* Statistics */
|
|
+ unsigned long rate_limit; /* Traffic shaping (NI) */
|
|
+ unsigned char threshold; /* TTL threshold */
|
|
+ unsigned short flags; /* Control flags */
|
|
+ int link; /* Physical interface index */
|
|
+};
|
|
+
|
|
+#define VIFF_STATIC 0x8000
|
|
+
|
|
+struct mfc6_cache
|
|
+{
|
|
+ struct mfc6_cache *next; /* Next entry on cache line */
|
|
+ struct in6_addr mf6c_mcastgrp; /* Group the entry belongs to */
|
|
+ struct in6_addr mf6c_origin; /* Source of packet */
|
|
+ mifi_t mf6c_parent; /* Source interface */
|
|
+ int mfc_flags; /* Flags on line */
|
|
+
|
|
+ union {
|
|
+ struct {
|
|
+ unsigned long expires;
|
|
+ struct sk_buff_head unresolved; /* Unresolved buffers */
|
|
+ } unres;
|
|
+ struct {
|
|
+ unsigned long last_assert;
|
|
+ int minvif;
|
|
+ int maxvif;
|
|
+ unsigned long bytes;
|
|
+ unsigned long pkt;
|
|
+ unsigned long wrong_if;
|
|
+ unsigned char ttls[MAXMIFS]; /* TTL thresholds */
|
|
+ } res;
|
|
+ } mfc_un;
|
|
+};
|
|
+
|
|
+#define MFC_STATIC 1
|
|
+#define MFC_NOTIFY 2
|
|
+
|
|
+#define MFC6_LINES 64
|
|
+
|
|
+#if (MFC6_LINES & (MFC6_LINES -1 )) == 0
|
|
+#define MF6CHASHMOD(h) ((h) & (MFC6_LINES -1))
|
|
+#else
|
|
+#define MF6CHASHMOD(h) ((h) % MFC6_LINES)
|
|
+#endif
|
|
+
|
|
+#define MFC6_HASH(a, g) MF6CHASHMOD((a).s6_addr32[0] ^ (a).s6_addr32[1] ^ \
|
|
+ (a).s6_addr32[2] ^ (a).s6_addr32[3] ^ \
|
|
+ (a).s6_addr32[0] ^ (a).s6_addr32[1] ^ \
|
|
+ (a).s6_addr32[2] ^ (a).s6_addr32[3])
|
|
+
|
|
+#endif
|
|
+
|
|
+
|
|
+
|
|
+#define MFC_ASSERT_THRESH (3*HZ) /* Maximal freq. of asserts */
|
|
+
|
|
+/*
|
|
+ * Pseudo messages used by mrouted
|
|
+ */
|
|
+
|
|
+#define IGMPMSG_NOCACHE 1 /* Kern cache fill request to mrouted */
|
|
+#define IGMPMSG_WRONGVIF 2 /* For PIM assert processing (unused) */
|
|
+#define IGMPMSG_WHOLEPKT 3 /* For PIM Register processing */
|
|
+
|
|
+#define PIM_REGISTER 1
|
|
+
|
|
+#ifdef __KERNEL__
|
|
+
|
|
+#define PIM_V1_VERSION __constant_htonl(0x10000000)
|
|
+#define PIM_V1_REGISTER 1
|
|
+
|
|
+#define PIM_VERSION 2
|
|
+
|
|
+#define PIM_NULL_REGISTER __constant_htonl(0x40000000)
|
|
+
|
|
+/* PIMv2 register message header layout (ietf-draft-idmr-pimvsm-v2-00.ps */
|
|
+
|
|
+struct pim6reghdr
|
|
+{
|
|
+ __u8 type;
|
|
+ __u8 reserved;
|
|
+ __u16 csum;
|
|
+ __u32 flags;
|
|
+};
|
|
+
|
|
+
|
|
+struct rtmsg;
|
|
+extern int ip6mr_get_route(struct sk_buff *skb, struct rtmsg *rtm, int nowait);
|
|
+#endif
|
|
+
|
|
+#ifdef __KERNEL__
|
|
+
|
|
+extern struct sock *mroute6_socket;
|
|
+
|
|
+#define IN6_ARE_ADDR_EQUAL(a,b) \
|
|
+ (memcmp(&(a)->s6_addr[0], &(b)->s6_addr[0], sizeof(struct in6_addr)) == 0)
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * Structure used to communicate from kernel to multicast router.
|
|
+ * We'll overlay the structure onto an MLD header (not an IPv6 heder like igmpmsg{}
|
|
+ * used for IPv4 implementation). This is because this structure will be passed via an
|
|
+ * IPv6 raw socket, on wich an application will only receiver the payload i.e the data after
|
|
+ * the IPv6 header and all the extension headers. (See section 3 of RFC 3542)
|
|
+ */
|
|
+
|
|
+struct mrt6msg {
|
|
+#define MRT6MSG_NOCACHE 1
|
|
+#define MRT6MSG_WRONGMIF 2
|
|
+#define MRT6MSG_WHOLEPKT 3 /* used for use level encap */
|
|
+ u_char im6_mbz; /* must be zero */
|
|
+ u_char im6_msgtype; /* what type of message */
|
|
+ u_int16_t im6_mif; /* mif rec'd on */
|
|
+ u_int32_t im6_pad; /* padding for 64 bit arch */
|
|
+ struct in6_addr im6_src, im6_dst;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * PIM packet header
|
|
+ */
|
|
+#define PIM_VERSION 2
|
|
+struct pim {
|
|
+#if defined(BYTE_ORDER) && (BYTE_ORDER == LITTLE_ENDIAN)
|
|
+ u_char pim_type:4, /* the PIM message type, currently they are:
|
|
+ * Hello, Register, Register-Stop, Join/Prune,
|
|
+ * Bootstrap, Assert, Graft (PIM-DM only),
|
|
+ * Graft-Ack (PIM-DM only), C-RP-Adv
|
|
+ */
|
|
+ pim_ver:4; /* PIM version number; 2 for PIMv2 */
|
|
+#else
|
|
+ u_char pim_ver:4, /* PIM version */
|
|
+ pim_type:4; /* PIM type */
|
|
+#endif
|
|
+ u_char pim_rsv; /* Reserved */
|
|
+ u_short pim_cksum; /* IP style check sum */
|
|
+};
|
|
+
|
|
+#define PIM_MINLEN 8 /* The header min. length is 8 */
|
|
+#define PIM6_REG_MINLEN (PIM_MINLEN+40) /* Register message + inner IP6 header */
|
|
+
|
|
+#define IPV6_VERSION 0x60
|
|
+#define IPV6_VERSION_MASK 0xf0
|
|
+
|
|
+/* XXX :there should not be there */
|
|
+#include <linux/icmpv6.h>
|
|
+
|
|
+struct mld_hdr {
|
|
+ struct icmp6hdr mld_icmp6_hdr;
|
|
+ struct in6_addr mld_addr;
|
|
+};
|
|
+
|
|
+#define mld_type mld_icmp6_hdr.icmp6_type
|
|
+
|
|
+#endif
|
|
diff -urN linux-2.6.11/include/linux/netfilter.h x1/include/linux/netfilter.h
|
|
--- linux-2.6.11/include/linux/netfilter.h 2005-03-02 08:38:09.000000000 +0100
|
|
+++ x1/include/linux/netfilter.h 2005-02-03 05:44:11.000000000 +0100
|
|
@@ -175,6 +175,10 @@
|
|
extern void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *);
|
|
extern void nf_ct_attach(struct sk_buff *, struct sk_buff *);
|
|
|
|
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
|
+extern void (*ip6_ct_attach)(struct sk_buff *, struct sk_buff *);
|
|
+#endif
|
|
+
|
|
/* FIXME: Before cache is ever used, this must be implemented for real. */
|
|
extern void nf_invalidate_cache(int pf);
|
|
|
|
diff -urN linux-2.6.11/include/linux/netfilter_ipv6/ip6_conntrack.h x1/include/linux/netfilter_ipv6/ip6_conntrack.h
|
|
--- linux-2.6.11/include/linux/netfilter_ipv6/ip6_conntrack.h 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/include/linux/netfilter_ipv6/ip6_conntrack.h 2004-10-01 11:24:38.000000000 +0200
|
|
@@ -0,0 +1,264 @@
|
|
+/*
|
|
+ * Copyright (C)2003 USAGI/WIDE Project
|
|
+ *
|
|
+ * Authors:
|
|
+ * Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
|
|
+ *
|
|
+ * Based on: include/linux/netfilter_ipv4/ip_conntrack.h
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+#ifndef _IP6_CONNTRACK_H
|
|
+#define _IP6_CONNTRACK_H
|
|
+/* Connection state tracking for netfilter. This is separated from,
|
|
+ but required by, the NAT layer; it can also be used by an iptables
|
|
+ extension. */
|
|
+
|
|
+#include <linux/config.h>
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack_tuple.h>
|
|
+#include <linux/bitops.h>
|
|
+#include <linux/compiler.h>
|
|
+#include <asm/atomic.h>
|
|
+
|
|
+enum ip6_conntrack_info
|
|
+{
|
|
+ /* Part of an established connection (either direction). */
|
|
+ IP6_CT_ESTABLISHED,
|
|
+
|
|
+ /* Like NEW, but related to an existing connection, or ICMP error
|
|
+ (in either direction). */
|
|
+ IP6_CT_RELATED,
|
|
+
|
|
+ /* Started a new connection to track (only
|
|
+ IP6_CT_DIR_ORIGINAL); may be a retransmission. */
|
|
+ IP6_CT_NEW,
|
|
+
|
|
+ /* >= this indicates reply direction */
|
|
+ IP6_CT_IS_REPLY,
|
|
+
|
|
+ /* Number of distinct IP6_CT types (no NEW in reply dirn). */
|
|
+ IP6_CT_NUMBER = IP6_CT_IS_REPLY * 2 - 1
|
|
+};
|
|
+
|
|
+/* Bitset representing status of connection. */
|
|
+enum ip6_conntrack_status {
|
|
+ /* It's an expected connection: bit 0 set. This bit never changed */
|
|
+ IP6S_EXPECTED_BIT = 0,
|
|
+ IP6S_EXPECTED = (1 << IP6S_EXPECTED_BIT),
|
|
+
|
|
+ /* We've seen packets both ways: bit 1 set. Can be set, not unset. */
|
|
+ IP6S_SEEN_REPLY_BIT = 1,
|
|
+ IP6S_SEEN_REPLY = (1 << IP6S_SEEN_REPLY_BIT),
|
|
+
|
|
+ /* Conntrack should never be early-expired. */
|
|
+ IP6S_ASSURED_BIT = 2,
|
|
+ IP6S_ASSURED = (1 << IP6S_ASSURED_BIT),
|
|
+
|
|
+ /* Connection is confirmed: originating packet has left box */
|
|
+ IP6S_CONFIRMED_BIT = 3,
|
|
+ IP6S_CONFIRMED = (1 << IP6S_CONFIRMED_BIT),
|
|
+};
|
|
+
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack_tcp.h>
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack_icmpv6.h>
|
|
+
|
|
+/* per conntrack: protocol private data */
|
|
+union ip6_conntrack_proto {
|
|
+ /* insert conntrack proto private data here */
|
|
+ struct ip6_ct_tcp tcp;
|
|
+ struct ip6_ct_icmpv6 icmpv6;
|
|
+};
|
|
+
|
|
+union ip6_conntrack_expect_proto {
|
|
+ /* insert expect proto private data here */
|
|
+};
|
|
+
|
|
+/* Add protocol helper include file here */
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack_ftp.h>
|
|
+
|
|
+/* per expectation: application helper private data */
|
|
+union ip6_conntrack_expect_help {
|
|
+ /* insert conntrack helper private data (expect) here */
|
|
+ struct ip6_ct_ftp_expect exp_ftp_info;
|
|
+};
|
|
+
|
|
+/* per conntrack: application helper private data */
|
|
+union ip6_conntrack_help {
|
|
+ /* insert conntrack helper private data (master) here */
|
|
+ struct ip6_ct_ftp_master ct_ftp_info;
|
|
+};
|
|
+
|
|
+#ifdef __KERNEL__
|
|
+
|
|
+#include <linux/types.h>
|
|
+#include <linux/skbuff.h>
|
|
+
|
|
+#ifdef CONFIG_NF_DEBUG
|
|
+#define IP6_NF_ASSERT(x) \
|
|
+do { \
|
|
+ if (!(x)) \
|
|
+ /* Wooah! I'm tripping my conntrack in a frenzy of \
|
|
+ netplay... */ \
|
|
+ printk("NF_IP6_ASSERT: %s:%i(%s)\n", \
|
|
+ __FILE__, __LINE__, __FUNCTION__); \
|
|
+} while(0)
|
|
+#else
|
|
+#define IP6_NF_ASSERT(x)
|
|
+#endif
|
|
+
|
|
+struct ip6_conntrack_expect
|
|
+{
|
|
+ /* Internal linked list (global expectation list) */
|
|
+ struct list_head list;
|
|
+
|
|
+ /* reference count */
|
|
+ atomic_t use;
|
|
+
|
|
+ /* expectation list for this master */
|
|
+ struct list_head expected_list;
|
|
+
|
|
+ /* The conntrack of the master connection */
|
|
+ struct ip6_conntrack *expectant;
|
|
+
|
|
+ /* The conntrack of the sibling connection, set after
|
|
+ * expectation arrived */
|
|
+ struct ip6_conntrack *sibling;
|
|
+
|
|
+ /* IPv6 packet is never NATed */
|
|
+ /* Tuple saved for conntrack */
|
|
+/*
|
|
+ struct ip6_conntrack_tuple ct_tuple;
|
|
+*/
|
|
+
|
|
+ /* Timer function; deletes the expectation. */
|
|
+ struct timer_list timeout;
|
|
+
|
|
+ /* Data filled out by the conntrack helpers follow: */
|
|
+
|
|
+ /* We expect this tuple, with the following mask */
|
|
+ struct ip6_conntrack_tuple tuple, mask;
|
|
+
|
|
+ /* Function to call after setup and insertion */
|
|
+ int (*expectfn)(struct ip6_conntrack *new);
|
|
+
|
|
+ /* At which sequence number did this expectation occur */
|
|
+ u_int32_t seq;
|
|
+
|
|
+ union ip6_conntrack_expect_proto proto;
|
|
+
|
|
+ union ip6_conntrack_expect_help help;
|
|
+};
|
|
+
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack_helper.h>
|
|
+struct ip6_conntrack
|
|
+{
|
|
+ /* Usage count in here is 1 for hash table/destruct timer, 1 per skb,
|
|
+ plus 1 for any connection(s) we are `master' for */
|
|
+ struct nf_conntrack ct_general;
|
|
+
|
|
+ /* These are my tuples; original and reply */
|
|
+ struct ip6_conntrack_tuple_hash tuplehash[IP6_CT_DIR_MAX];
|
|
+
|
|
+ /* Have we seen traffic both ways yet? (bitset) */
|
|
+ unsigned long status;
|
|
+
|
|
+ /* Timer function; drops refcnt when it goes off. */
|
|
+ struct timer_list timeout;
|
|
+
|
|
+ /* If we're expecting another related connection, this will be
|
|
+ in expected linked list */
|
|
+ struct list_head sibling_list;
|
|
+
|
|
+ /* Current number of expected connections */
|
|
+ unsigned int expecting;
|
|
+
|
|
+ /* If we were expected by an expectation, this will be it */
|
|
+ struct ip6_conntrack_expect *master;
|
|
+
|
|
+ /* Helper, if any. */
|
|
+ struct ip6_conntrack_helper *helper;
|
|
+
|
|
+ /* Storage reserved for other modules: */
|
|
+ union ip6_conntrack_proto proto;
|
|
+
|
|
+ union ip6_conntrack_help help;
|
|
+};
|
|
+
|
|
+/* get master conntrack via master expectation */
|
|
+#define master_ct6(conntr) (conntr->master ? conntr->master->expectant : NULL)
|
|
+
|
|
+/* Alter reply tuple (maybe alter helper). If it's already taken,
|
|
+ return 0 and don't do alteration. */
|
|
+extern int
|
|
+ip6_conntrack_alter_reply(struct ip6_conntrack *conntrack,
|
|
+ const struct ip6_conntrack_tuple *newreply);
|
|
+
|
|
+/* Is this tuple taken? (ignoring any belonging to the given
|
|
+ conntrack). */
|
|
+extern int
|
|
+ip6_conntrack_tuple_taken(const struct ip6_conntrack_tuple *tuple,
|
|
+ const struct ip6_conntrack *ignored_conntrack);
|
|
+
|
|
+/* Return conntrack_info and tuple hash for given skb. */
|
|
+static inline struct ip6_conntrack *
|
|
+ip6_conntrack_get(const struct sk_buff *skb, enum ip6_conntrack_info *ctinfo)
|
|
+{
|
|
+ *ctinfo = skb->nfctinfo;
|
|
+ return (struct ip6_conntrack *)skb->nfct;
|
|
+}
|
|
+
|
|
+/* decrement reference count on a conntrack */
|
|
+extern inline void ip6_conntrack_put(struct ip6_conntrack *ct);
|
|
+
|
|
+/* find unconfirmed expectation based on tuple */
|
|
+struct ip6_conntrack_expect *
|
|
+ip6_conntrack_expect_find_get(const struct ip6_conntrack_tuple *tuple);
|
|
+
|
|
+/* decrement reference count on an expectation */
|
|
+void ip6_conntrack_expect_put(struct ip6_conntrack_expect *exp);
|
|
+
|
|
+/* call to create an explicit dependency on ip6_conntrack. */
|
|
+extern void need_ip6_conntrack(void);
|
|
+
|
|
+extern int ip6_invert_tuplepr(struct ip6_conntrack_tuple *inverse,
|
|
+ const struct ip6_conntrack_tuple *orig);
|
|
+
|
|
+/* Refresh conntrack for this many jiffies */
|
|
+extern void ip6_ct_refresh(struct ip6_conntrack *ct,
|
|
+ unsigned long extra_jiffies);
|
|
+
|
|
+/* Call me when a conntrack is destroyed. */
|
|
+extern void (*ip6_conntrack_destroyed)(struct ip6_conntrack *conntrack);
|
|
+
|
|
+/* Returns new sk_buff, or NULL */
|
|
+struct sk_buff *
|
|
+ip6_ct_gather_frags(struct sk_buff *skb);
|
|
+
|
|
+/* Delete all conntracks which match. */
|
|
+extern void
|
|
+ip6_ct_selective_cleanup(int (*kill)(const struct ip6_conntrack *i, void *data),
|
|
+ void *data);
|
|
+
|
|
+/* It's confirmed if it is, or has been in the hash table. */
|
|
+static inline int is_confirmed(struct ip6_conntrack *ct)
|
|
+{
|
|
+ return test_bit(IP6S_CONFIRMED_BIT, &ct->status);
|
|
+}
|
|
+
|
|
+extern unsigned int ip6_conntrack_htable_size;
|
|
+
|
|
+/* eg. PROVIDES_CONNTRACK6(ftp); */
|
|
+#define PROVIDES_CONNTRACK6(name) \
|
|
+ int needs_ip6_conntrack_##name; \
|
|
+ EXPORT_SYMBOL(needs_ip6_conntrack_##name)
|
|
+
|
|
+/*. eg. NEEDS_CONNTRACK6(ftp); */
|
|
+#define NEEDS_CONNTRACK6(name) \
|
|
+ extern int needs_ip6_conntrack_##name; \
|
|
+ static int *need_ip6_conntrack_##name __attribute_used__ = &needs_ip6_conntrack_##name
|
|
+
|
|
+#endif /* __KERNEL__ */
|
|
+#endif /* _IP6_CONNTRACK_H */
|
|
diff -urN linux-2.6.11/include/linux/netfilter_ipv6/ip6_conntrack_core.h x1/include/linux/netfilter_ipv6/ip6_conntrack_core.h
|
|
--- linux-2.6.11/include/linux/netfilter_ipv6/ip6_conntrack_core.h 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/include/linux/netfilter_ipv6/ip6_conntrack_core.h 2004-10-01 11:24:38.000000000 +0200
|
|
@@ -0,0 +1,69 @@
|
|
+/*
|
|
+ * Copyright (C)2003 USAGI/WIDE Project
|
|
+ *
|
|
+ * Authors:
|
|
+ * Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
|
|
+ *
|
|
+ * Based on: include/linux/netfilter_ipv4/ip_conntrack_core.h
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+#ifndef _IP6_CONNTRACK_CORE_H
|
|
+#define _IP6_CONNTRACK_CORE_H
|
|
+#include <linux/netfilter.h>
|
|
+#include <linux/netfilter_ipv4/lockhelp.h>
|
|
+
|
|
+/* This header is used to share core functionality between the
|
|
+ standalone connection tracking module, and the compatibility layer's use
|
|
+ of connection tracking. */
|
|
+extern unsigned int ip6_conntrack_in(unsigned int hooknum,
|
|
+ struct sk_buff **pskb,
|
|
+ const struct net_device *in,
|
|
+ const struct net_device *out,
|
|
+ int (*okfn)(struct sk_buff *));
|
|
+
|
|
+extern int ip6_conntrack_init(void);
|
|
+extern void ip6_conntrack_cleanup(void);
|
|
+
|
|
+struct ip6_conntrack_protocol;
|
|
+extern struct ip6_conntrack_protocol *ip6_ct_find_proto(u_int8_t protocol);
|
|
+/* Like above, but you already have conntrack read lock. */
|
|
+extern struct ip6_conntrack_protocol *__ip6_ct_find_proto(u_int8_t protocol);
|
|
+extern struct list_head ip6_protocol_list;
|
|
+
|
|
+/* Returns conntrack if it dealt with ICMP, and filled in skb->nfct */
|
|
+extern struct ip6_conntrack *icmp6_error_track(struct sk_buff *skb,
|
|
+ unsigned int icmp6off,
|
|
+ enum ip6_conntrack_info *ctinfo,
|
|
+ unsigned int hooknum);
|
|
+extern int ip6_get_tuple(const struct ipv6hdr *ipv6h,
|
|
+ const struct sk_buff *skb,
|
|
+ unsigned int protoff,
|
|
+ u_int8_t protonum,
|
|
+ struct ip6_conntrack_tuple *tuple,
|
|
+ const struct ip6_conntrack_protocol *protocol);
|
|
+
|
|
+/* Find a connection corresponding to a tuple. */
|
|
+struct ip6_conntrack_tuple_hash *
|
|
+ip6_conntrack_find_get(const struct ip6_conntrack_tuple *tuple,
|
|
+ const struct ip6_conntrack *ignored_conntrack);
|
|
+
|
|
+extern int __ip6_conntrack_confirm(struct sk_buff *skb);
|
|
+
|
|
+/* Confirm a connection: returns NF_DROP if packet must be dropped. */
|
|
+static inline int ip6_conntrack_confirm(struct sk_buff *skb)
|
|
+{
|
|
+ if (skb->nfct
|
|
+ && !is_confirmed((struct ip6_conntrack *)skb->nfct))
|
|
+ return __ip6_conntrack_confirm(skb);
|
|
+ return NF_ACCEPT;
|
|
+}
|
|
+
|
|
+extern struct list_head *ip6_conntrack_hash;
|
|
+extern struct list_head ip6_conntrack_expect_list;
|
|
+DECLARE_RWLOCK_EXTERN(ip6_conntrack_lock);
|
|
+#endif /* _IP6_CONNTRACK_CORE_H */
|
|
+
|
|
diff -urN linux-2.6.11/include/linux/netfilter_ipv6/ip6_conntrack_ftp.h x1/include/linux/netfilter_ipv6/ip6_conntrack_ftp.h
|
|
--- linux-2.6.11/include/linux/netfilter_ipv6/ip6_conntrack_ftp.h 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/include/linux/netfilter_ipv6/ip6_conntrack_ftp.h 2003-09-18 12:48:46.000000000 +0200
|
|
@@ -0,0 +1,57 @@
|
|
+/*
|
|
+ * Copyright (C)2003 USAGI/WIDE Project
|
|
+ *
|
|
+ * Authors:
|
|
+ * Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
|
|
+ *
|
|
+ * Based on: include/linux/netfilter_ipv4/ip_conntrack_ftp.h
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+#ifndef _IP6_CONNTRACK_FTP_H
|
|
+#define _IP6_CONNTRACK_FTP_H
|
|
+/* FTP tracking. */
|
|
+
|
|
+#ifdef __KERNEL__
|
|
+
|
|
+#include <linux/netfilter_ipv4/lockhelp.h>
|
|
+
|
|
+/* Protects ftp part of conntracks */
|
|
+DECLARE_LOCK_EXTERN(ip6_ftp_lock);
|
|
+
|
|
+#define FTP_PORT 21
|
|
+
|
|
+#endif /* __KERNEL__ */
|
|
+
|
|
+enum ip6_ct_ftp_type
|
|
+{
|
|
+ /* EPRT command from client */
|
|
+ IP6_CT_FTP_EPRT,
|
|
+ /* EPSV response from server */
|
|
+ IP6_CT_FTP_EPSV,
|
|
+};
|
|
+
|
|
+/* This structure is per expected connection */
|
|
+struct ip6_ct_ftp_expect
|
|
+{
|
|
+ /* We record seq number and length of ftp ip/port text here: all in
|
|
+ * host order. */
|
|
+
|
|
+ /* sequence number of IP address in packet is in ip_conntrack_expect */
|
|
+ u_int32_t len; /* length of IPv6 address */
|
|
+ enum ip6_ct_ftp_type ftptype; /* EPRT or EPSV ? */
|
|
+ u_int16_t port; /* Port that was to be used */
|
|
+};
|
|
+
|
|
+/* This structure exists only once per master */
|
|
+struct ip6_ct_ftp_master {
|
|
+ /* Next valid seq position for cmd matching after newline */
|
|
+ u_int32_t seq_aft_nl[IP6_CT_DIR_MAX];
|
|
+ /* 0 means seq_match_aft_nl not set */
|
|
+ int seq_aft_nl_set[IP6_CT_DIR_MAX];
|
|
+};
|
|
+
|
|
+#endif /* _IP6_CONNTRACK_FTP_H */
|
|
diff -urN linux-2.6.11/include/linux/netfilter_ipv6/ip6_conntrack_helper.h x1/include/linux/netfilter_ipv6/ip6_conntrack_helper.h
|
|
--- linux-2.6.11/include/linux/netfilter_ipv6/ip6_conntrack_helper.h 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/include/linux/netfilter_ipv6/ip6_conntrack_helper.h 2003-09-18 12:48:46.000000000 +0200
|
|
@@ -0,0 +1,57 @@
|
|
+/*
|
|
+ * Copyright (C)2003 USAGI/WIDE Project
|
|
+ *
|
|
+ * Authors:
|
|
+ * Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
|
|
+ *
|
|
+ * Based on: include/linux/netfilter_ipv4/ip_conntrack_helper.h
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+/* IP6 connection tracking helpers. */
|
|
+#ifndef _IP6_CONNTRACK_HELPER_H
|
|
+#define _IP6_CONNTRACK_HELPER_H
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack.h>
|
|
+
|
|
+struct module;
|
|
+
|
|
+/* Reuse expectation when max_expected reached */
|
|
+#define IP6_CT_HELPER_F_REUSE_EXPECT 0x01
|
|
+
|
|
+struct ip6_conntrack_helper
|
|
+{
|
|
+ struct list_head list; /* Internal use. */
|
|
+
|
|
+ const char *name; /* name of the module */
|
|
+ unsigned char flags; /* Flags (see above) */
|
|
+ struct module *me; /* pointer to self */
|
|
+ unsigned int max_expected; /* Maximum number of concurrent
|
|
+ * expected connections */
|
|
+ unsigned int timeout; /* timeout for expecteds */
|
|
+
|
|
+ /* Mask of things we will help (compared against server response) */
|
|
+ struct ip6_conntrack_tuple tuple;
|
|
+ struct ip6_conntrack_tuple mask;
|
|
+
|
|
+ /* Function to call when data passes; return verdict, or -1 to
|
|
+ invalidate. */
|
|
+ int (*help)(const struct sk_buff *skb,
|
|
+ unsigned int protoff,
|
|
+ struct ip6_conntrack *ct,
|
|
+ enum ip6_conntrack_info conntrackinfo);
|
|
+};
|
|
+
|
|
+extern int ip6_conntrack_helper_register(struct ip6_conntrack_helper *);
|
|
+extern void ip6_conntrack_helper_unregister(struct ip6_conntrack_helper *);
|
|
+
|
|
+extern struct ip6_conntrack_helper *ip6_ct_find_helper(const struct ip6_conntrack_tuple *tuple);
|
|
+
|
|
+/* Add an expected connection: can have more than one per connection */
|
|
+extern int ip6_conntrack_expect_related(struct ip6_conntrack *related_to,
|
|
+ struct ip6_conntrack_expect *exp);
|
|
+extern void ip6_conntrack_unexpect_related(struct ip6_conntrack_expect *exp);
|
|
+
|
|
+#endif /*_IP6_CONNTRACK_HELPER_H*/
|
|
diff -urN linux-2.6.11/include/linux/netfilter_ipv6/ip6_conntrack_icmpv6.h x1/include/linux/netfilter_ipv6/ip6_conntrack_icmpv6.h
|
|
--- linux-2.6.11/include/linux/netfilter_ipv6/ip6_conntrack_icmpv6.h 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/include/linux/netfilter_ipv6/ip6_conntrack_icmpv6.h 2003-09-18 12:48:46.000000000 +0200
|
|
@@ -0,0 +1,24 @@
|
|
+/*
|
|
+ * Copyright (C)2003 USAGI/WIDE Project
|
|
+ *
|
|
+ * Authors:
|
|
+ * Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
|
|
+ *
|
|
+ * Based on: include/linux/netfilter_ipv4/ip_conntrack_icmp.h
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+#ifndef _IP6_CONNTRACK_ICMPV6_H
|
|
+#define _IP6_CONNTRACK_ICMPV6_H
|
|
+/* ICMPv6 tracking. */
|
|
+#include <asm/atomic.h>
|
|
+
|
|
+struct ip6_ct_icmpv6
|
|
+{
|
|
+ /* Optimization: when number in == number out, forget immediately. */
|
|
+ atomic_t count;
|
|
+};
|
|
+#endif /* _IP6_CONNTRACK_ICMPv6_H */
|
|
diff -urN linux-2.6.11/include/linux/netfilter_ipv6/ip6_conntrack_protocol.h x1/include/linux/netfilter_ipv6/ip6_conntrack_protocol.h
|
|
--- linux-2.6.11/include/linux/netfilter_ipv6/ip6_conntrack_protocol.h 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/include/linux/netfilter_ipv6/ip6_conntrack_protocol.h 2003-09-18 12:48:46.000000000 +0200
|
|
@@ -0,0 +1,83 @@
|
|
+/*
|
|
+ * Copyright (C)2003 USAGI/WIDE Project
|
|
+ *
|
|
+ * Authors:
|
|
+ * Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
|
|
+ *
|
|
+ * Based on: include/linux/netfilter_ipv4/ip_conntrack_protocol.h
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+/* Header for use in defining a given protocol for connection tracking. */
|
|
+#ifndef _IP6_CONNTRACK_PROTOCOL_H
|
|
+#define _IP6_CONNTRACK_PROTOCOL_H
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack.h>
|
|
+#include <linux/skbuff.h>
|
|
+
|
|
+struct ip6_conntrack_protocol
|
|
+{
|
|
+ /* Next pointer. */
|
|
+ struct list_head list;
|
|
+
|
|
+ /* Protocol number. */
|
|
+ u_int8_t proto;
|
|
+
|
|
+ /* Protocol name */
|
|
+ const char *name;
|
|
+
|
|
+ /* Try to fill in the third arg: dataoff is offset past IPv6
|
|
+ hdr and IPv6 ext hdrs. Return true if possible. */
|
|
+ int (*pkt_to_tuple)(const struct sk_buff *skb,
|
|
+ unsigned int dataoff,
|
|
+ struct ip6_conntrack_tuple *tuple);
|
|
+
|
|
+ /* Invert the per-proto part of the tuple: ie. turn xmit into reply.
|
|
+ * Some packets can't be inverted: return 0 in that case.
|
|
+ */
|
|
+ int (*invert_tuple)(struct ip6_conntrack_tuple *inverse,
|
|
+ const struct ip6_conntrack_tuple *orig);
|
|
+
|
|
+ /* Print out the per-protocol part of the tuple. */
|
|
+ unsigned int (*print_tuple)(char *buffer,
|
|
+ const struct ip6_conntrack_tuple *);
|
|
+
|
|
+ /* Print out the private part of the conntrack. */
|
|
+ unsigned int (*print_conntrack)(char *buffer,
|
|
+ const struct ip6_conntrack *);
|
|
+
|
|
+ /* Returns verdict for packet, or -1 for invalid. */
|
|
+ int (*packet)(struct ip6_conntrack *conntrack,
|
|
+ const struct sk_buff *skb,
|
|
+ unsigned int dataoff,
|
|
+ enum ip6_conntrack_info ctinfo);
|
|
+
|
|
+ /* Called when a new connection for this protocol found;
|
|
+ * returns TRUE if it's OK. If so, packet() called next. */
|
|
+ int (*new)(struct ip6_conntrack *conntrack, const struct sk_buff *skb,
|
|
+ unsigned int dataoff);
|
|
+
|
|
+ /* Called when a conntrack entry is destroyed */
|
|
+ void (*destroy)(struct ip6_conntrack *conntrack);
|
|
+
|
|
+ /* Has to decide if a expectation matches one packet or not */
|
|
+ int (*exp_matches_pkt)(struct ip6_conntrack_expect *exp,
|
|
+ const struct sk_buff *skb,
|
|
+ unsigned int dataoff);
|
|
+
|
|
+ /* Module (if any) which this is connected to. */
|
|
+ struct module *me;
|
|
+};
|
|
+
|
|
+/* Protocol registration. */
|
|
+extern int ip6_conntrack_protocol_register(struct ip6_conntrack_protocol *proto);
|
|
+extern void ip6_conntrack_protocol_unregister(struct ip6_conntrack_protocol *proto);
|
|
+
|
|
+/* Existing built-in protocols */
|
|
+extern struct ip6_conntrack_protocol ip6_conntrack_protocol_tcp;
|
|
+extern struct ip6_conntrack_protocol ip6_conntrack_protocol_udp;
|
|
+extern struct ip6_conntrack_protocol ip6_conntrack_protocol_icmpv6;
|
|
+extern int ip6_conntrack_protocol_tcp_init(void);
|
|
+#endif /*_IP6_CONNTRACK_PROTOCOL_H*/
|
|
diff -urN linux-2.6.11/include/linux/netfilter_ipv6/ip6_conntrack_reasm.h x1/include/linux/netfilter_ipv6/ip6_conntrack_reasm.h
|
|
--- linux-2.6.11/include/linux/netfilter_ipv6/ip6_conntrack_reasm.h 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/include/linux/netfilter_ipv6/ip6_conntrack_reasm.h 2003-09-18 12:48:46.000000000 +0200
|
|
@@ -0,0 +1,28 @@
|
|
+/*
|
|
+ * Copyright (C)2003 USAGI/WIDE Project
|
|
+ *
|
|
+ * Authors:
|
|
+ * Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+#ifndef _IP6_CONNTRACK_REASM_H
|
|
+#define _IP6_CONNTRACK_REASM_H
|
|
+
|
|
+#include <linux/netfilter.h>
|
|
+extern struct sk_buff *
|
|
+ip6_ct_gather_frags(struct sk_buff *skb);
|
|
+
|
|
+extern int
|
|
+ip6_ct_output_frags(struct sk_buff *skb, struct nf_info *info);
|
|
+
|
|
+extern int ip6_ct_kfree_frags(struct sk_buff *skb);
|
|
+
|
|
+extern int ip6_ct_frags_init(void);
|
|
+extern void ip6_ct_frags_cleanup(void);
|
|
+
|
|
+#endif /* _IP6_CONNTRACK_REASM_H */
|
|
+
|
|
diff -urN linux-2.6.11/include/linux/netfilter_ipv6/ip6_conntrack_tcp.h x1/include/linux/netfilter_ipv6/ip6_conntrack_tcp.h
|
|
--- linux-2.6.11/include/linux/netfilter_ipv6/ip6_conntrack_tcp.h 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/include/linux/netfilter_ipv6/ip6_conntrack_tcp.h 2003-09-18 12:48:46.000000000 +0200
|
|
@@ -0,0 +1,41 @@
|
|
+/*
|
|
+ * Copyright (C)2003 USAGI/WIDE Project
|
|
+ *
|
|
+ * Authors:
|
|
+ * Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
|
|
+ *
|
|
+ * Based on: include/linux/netfilter_ipv4/ip_conntrack_tcp.h
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+#ifndef _IP6_CONNTRACK_TCP_H
|
|
+#define _IP6_CONNTRACK_TCP_H
|
|
+/* TCP tracking. */
|
|
+
|
|
+enum tcp_conntrack {
|
|
+ TCP_CONNTRACK_NONE,
|
|
+ TCP_CONNTRACK_ESTABLISHED,
|
|
+ TCP_CONNTRACK_SYN_SENT,
|
|
+ TCP_CONNTRACK_SYN_RECV,
|
|
+ TCP_CONNTRACK_FIN_WAIT,
|
|
+ TCP_CONNTRACK_TIME_WAIT,
|
|
+ TCP_CONNTRACK_CLOSE,
|
|
+ TCP_CONNTRACK_CLOSE_WAIT,
|
|
+ TCP_CONNTRACK_LAST_ACK,
|
|
+ TCP_CONNTRACK_LISTEN,
|
|
+ TCP_CONNTRACK_MAX
|
|
+};
|
|
+
|
|
+struct ip6_ct_tcp
|
|
+{
|
|
+ enum tcp_conntrack state;
|
|
+
|
|
+ /* Poor man's window tracking: sequence number of valid ACK
|
|
+ handshake completion packet */
|
|
+ u_int32_t handshake_ack;
|
|
+};
|
|
+
|
|
+#endif /* _IP6_CONNTRACK_TCP_H */
|
|
diff -urN linux-2.6.11/include/linux/netfilter_ipv6/ip6_conntrack_tuple.h x1/include/linux/netfilter_ipv6/ip6_conntrack_tuple.h
|
|
--- linux-2.6.11/include/linux/netfilter_ipv6/ip6_conntrack_tuple.h 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/include/linux/netfilter_ipv6/ip6_conntrack_tuple.h 2003-09-18 12:48:46.000000000 +0200
|
|
@@ -0,0 +1,131 @@
|
|
+/*
|
|
+ * Copyright (C)2003 USAGI/WIDE Project
|
|
+ *
|
|
+ * Authors:
|
|
+ * Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
|
|
+ *
|
|
+ * Based on: include/linux/netfilter_ipv4/ip_conntrack_tuple.h
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+#ifndef _IP6_CONNTRACK_TUPLE_H
|
|
+#define _IP6_CONNTRACK_TUPLE_H
|
|
+
|
|
+#ifdef __KERNEL__
|
|
+#include <linux/in6.h>
|
|
+#include <linux/kernel.h>
|
|
+#endif
|
|
+
|
|
+/* A `tuple' is a structure containing the information to uniquely
|
|
+ identify a connection. ie. if two packets have the same tuple, they
|
|
+ are in the same connection; if not, they are not.
|
|
+
|
|
+ We divide the structure along "manipulatable" and
|
|
+ "non-manipulatable" lines, for the benefit of the NAT code.
|
|
+*/
|
|
+
|
|
+/* The protocol-specific manipulable parts of the tuple: always in
|
|
+ network order! */
|
|
+union ip6_conntrack_manip_proto
|
|
+{
|
|
+ /* Add other protocols here. */
|
|
+ u_int16_t all;
|
|
+
|
|
+ struct {
|
|
+ u_int16_t port;
|
|
+ } tcp;
|
|
+ struct {
|
|
+ u_int16_t port;
|
|
+ } udp;
|
|
+ struct {
|
|
+ u_int16_t id;
|
|
+ } icmpv6;
|
|
+};
|
|
+
|
|
+/* The manipulable part of the tuple. */
|
|
+struct ip6_conntrack_manip
|
|
+{
|
|
+ struct in6_addr ip;
|
|
+ union ip6_conntrack_manip_proto u;
|
|
+};
|
|
+
|
|
+/* This contains the information to distinguish a connection. */
|
|
+struct ip6_conntrack_tuple
|
|
+{
|
|
+ struct ip6_conntrack_manip src;
|
|
+
|
|
+ /* These are the parts of the tuple which are fixed. */
|
|
+ struct {
|
|
+ struct in6_addr ip;
|
|
+ union {
|
|
+ /* Add other protocols here. */
|
|
+ u_int16_t all;
|
|
+
|
|
+ struct {
|
|
+ u_int16_t port;
|
|
+ } tcp;
|
|
+ struct {
|
|
+ u_int16_t port;
|
|
+ } udp;
|
|
+ struct {
|
|
+ u_int8_t type, code;
|
|
+ } icmpv6;
|
|
+ } u;
|
|
+
|
|
+ /* The protocol. */
|
|
+ u_int16_t protonum;
|
|
+ } dst;
|
|
+};
|
|
+
|
|
+enum ip6_conntrack_dir
|
|
+{
|
|
+ IP6_CT_DIR_ORIGINAL,
|
|
+ IP6_CT_DIR_REPLY,
|
|
+ IP6_CT_DIR_MAX
|
|
+};
|
|
+
|
|
+#ifdef __KERNEL__
|
|
+
|
|
+#define DUMP_TUPLE(tp) \
|
|
+{ \
|
|
+ DEBUGP("tuple %p: %u %x:%x:%x:%x:%x:%x:%x:%x, %hu -> %x:%x:%x:%x:%x:%x:%x:%x, %hu\n", \
|
|
+ (tp), (tp)->dst.protonum, \
|
|
+ NIP6((tp)->src.ip), ntohs((tp)->src.u.all), \
|
|
+ NIP6((tp)->dst.ip), ntohs((tp)->dst.u.all)); \
|
|
+}
|
|
+
|
|
+#define CTINFO2DIR(ctinfo) ((ctinfo) >= IP6_CT_IS_REPLY ? IP6_CT_DIR_REPLY : IP6_CT_DIR_ORIGINAL)
|
|
+
|
|
+/* If we're the first tuple, it's the original dir. */
|
|
+#define DIRECTION(h) ((enum ip6_conntrack_dir)(&(h)->ctrack->tuplehash[1] == (h)))
|
|
+
|
|
+/* Connections have two entries in the hash table: one for each way */
|
|
+struct ip6_conntrack_tuple_hash
|
|
+{
|
|
+ struct list_head list;
|
|
+
|
|
+ struct ip6_conntrack_tuple tuple;
|
|
+
|
|
+ /* this == &ctrack->tuplehash[DIRECTION(this)]. */
|
|
+ struct ip6_conntrack *ctrack;
|
|
+};
|
|
+
|
|
+#endif /* __KERNEL__ */
|
|
+
|
|
+extern int ip6_ct_tuple_src_equal(const struct ip6_conntrack_tuple *t1,
|
|
+ const struct ip6_conntrack_tuple *t2);
|
|
+
|
|
+extern int ip6_ct_tuple_dst_equal(const struct ip6_conntrack_tuple *t1,
|
|
+ const struct ip6_conntrack_tuple *t2);
|
|
+
|
|
+extern int ip6_ct_tuple_equal(const struct ip6_conntrack_tuple *t1,
|
|
+ const struct ip6_conntrack_tuple *t2);
|
|
+
|
|
+extern int ip6_ct_tuple_mask_cmp(const struct ip6_conntrack_tuple *t,
|
|
+ const struct ip6_conntrack_tuple *tuple,
|
|
+ const struct ip6_conntrack_tuple *mask);
|
|
+
|
|
+#endif /* _IP6_CONNTRACK_TUPLE_H */
|
|
diff -urN linux-2.6.11/include/linux/netfilter_ipv6/ip6t_REJECT.h x1/include/linux/netfilter_ipv6/ip6t_REJECT.h
|
|
--- linux-2.6.11/include/linux/netfilter_ipv6/ip6t_REJECT.h 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/include/linux/netfilter_ipv6/ip6t_REJECT.h 2004-09-20 03:55:57.000000000 +0200
|
|
@@ -0,0 +1,18 @@
|
|
+#ifndef _IP6T_REJECT_H
|
|
+#define _IP6T_REJECT_H
|
|
+
|
|
+enum ip6t_reject_with {
|
|
+ IP6T_ICMP6_NO_ROUTE,
|
|
+ IP6T_ICMP6_ADM_PROHIBITED,
|
|
+ IP6T_ICMP6_NOT_NEIGHBOUR,
|
|
+ IP6T_ICMP6_ADDR_UNREACH,
|
|
+ IP6T_ICMP6_PORT_UNREACH,
|
|
+ IP6T_ICMP6_ECHOREPLY,
|
|
+ IP6T_TCP_RESET
|
|
+};
|
|
+
|
|
+struct ip6t_reject_info {
|
|
+ enum ip6t_reject_with with; /* reject type */
|
|
+};
|
|
+
|
|
+#endif /*_IP6T_REJECT_H*/
|
|
diff -urN linux-2.6.11/include/linux/netfilter_ipv6/ip6t_state.h x1/include/linux/netfilter_ipv6/ip6t_state.h
|
|
--- linux-2.6.11/include/linux/netfilter_ipv6/ip6t_state.h 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/include/linux/netfilter_ipv6/ip6t_state.h 2003-09-18 12:48:46.000000000 +0200
|
|
@@ -0,0 +1,24 @@
|
|
+/*
|
|
+ * Copyright (C)2003 USAGI/WIDE Project
|
|
+ *
|
|
+ * Authors:
|
|
+ * Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
|
|
+ *
|
|
+ * Based on: include/linux/netfilter_ipv4/ipt_state.h
|
|
+ *
|
|
+ * 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.
|
|
+ */
|
|
+#ifndef _IP6T_STATE_H
|
|
+#define _IP6T_STATE_H
|
|
+
|
|
+#define IP6T_STATE_BIT(ctinfo) (1 << ((ctinfo)%IP6_CT_IS_REPLY+1))
|
|
+#define IP6T_STATE_INVALID (1 << 0)
|
|
+
|
|
+struct ip6t_state_info
|
|
+{
|
|
+ unsigned int statemask;
|
|
+};
|
|
+#endif /*_IP6T_STATE_H*/
|
|
diff -urN linux-2.6.11/include/linux/rtnetlink.h x1/include/linux/rtnetlink.h
|
|
--- linux-2.6.11/include/linux/rtnetlink.h 2005-03-02 08:38:18.000000000 +0100
|
|
+++ x1/include/linux/rtnetlink.h 2005-02-11 17:24:31.000000000 +0100
|
|
@@ -346,6 +346,7 @@
|
|
#define RTAX_FEATURE_ECN 0x00000001
|
|
#define RTAX_FEATURE_SACK 0x00000002
|
|
#define RTAX_FEATURE_TIMESTAMP 0x00000004
|
|
+#define RTAX_FEATURE_ALLFRAG 0x00000008
|
|
|
|
struct rta_session
|
|
{
|
|
diff -urN linux-2.6.11/include/linux/sysctl.h x1/include/linux/sysctl.h
|
|
--- linux-2.6.11/include/linux/sysctl.h 2005-03-02 08:38:10.000000000 +0100
|
|
+++ x1/include/linux/sysctl.h 2005-02-28 07:45:54.000000000 +0100
|
|
@@ -455,7 +455,8 @@
|
|
NET_IPV6_ROUTE_GC_INTERVAL=6,
|
|
NET_IPV6_ROUTE_GC_ELASTICITY=7,
|
|
NET_IPV6_ROUTE_MTU_EXPIRES=8,
|
|
- NET_IPV6_ROUTE_MIN_ADVMSS=9
|
|
+ NET_IPV6_ROUTE_MIN_ADVMSS=9,
|
|
+ NET_IPV6_ROUTE_GC_MIN_INTERVAL_MS=10
|
|
};
|
|
|
|
enum {
|
|
@@ -475,7 +476,8 @@
|
|
NET_IPV6_REGEN_MAX_RETRY=14,
|
|
NET_IPV6_MAX_DESYNC_FACTOR=15,
|
|
NET_IPV6_MAX_ADDRESSES=16,
|
|
- NET_IPV6_FORCE_MLD_VERSION=17
|
|
+ NET_IPV6_FORCE_MLD_VERSION=17,
|
|
+ NET_IPV6_MC_FORWARDING=18
|
|
};
|
|
|
|
/* /proc/sys/net/ipv6/icmp */
|
|
@@ -488,8 +490,8 @@
|
|
NET_NEIGH_MCAST_SOLICIT=1,
|
|
NET_NEIGH_UCAST_SOLICIT=2,
|
|
NET_NEIGH_APP_SOLICIT=3,
|
|
- NET_NEIGH_RETRANS_TIME=4,
|
|
- NET_NEIGH_REACHABLE_TIME=5,
|
|
+ NET_NEIGH_RETRANS_TIME=4, /* deprecated */
|
|
+ NET_NEIGH_REACHABLE_TIME=5, /* deprecated */
|
|
NET_NEIGH_DELAY_PROBE_TIME=6,
|
|
NET_NEIGH_GC_STALE_TIME=7,
|
|
NET_NEIGH_UNRES_QLEN=8,
|
|
@@ -500,7 +502,10 @@
|
|
NET_NEIGH_GC_INTERVAL=13,
|
|
NET_NEIGH_GC_THRESH1=14,
|
|
NET_NEIGH_GC_THRESH2=15,
|
|
- NET_NEIGH_GC_THRESH3=16
|
|
+ NET_NEIGH_GC_THRESH3=16,
|
|
+ NET_NEIGH_RETRANS_TIME_MS=17,
|
|
+ NET_NEIGH_REACHABLE_TIME_MS=18,
|
|
+ __NET_NEIGH_MAX
|
|
};
|
|
|
|
/* /proc/sys/net/ipx */
|
|
diff -urN linux-2.6.11/include/linux/xfrm.h x1/include/linux/xfrm.h
|
|
--- linux-2.6.11/include/linux/xfrm.h 2005-03-02 08:38:37.000000000 +0100
|
|
+++ x1/include/linux/xfrm.h 2005-01-21 06:15:37.000000000 +0100
|
|
@@ -90,8 +90,12 @@
|
|
{
|
|
XFRM_POLICY_IN = 0,
|
|
XFRM_POLICY_OUT = 1,
|
|
+#ifdef CONFIG_USE_POLICY_FWD
|
|
XFRM_POLICY_FWD = 2,
|
|
XFRM_POLICY_MAX = 3
|
|
+#else
|
|
+ XFRM_POLICY_MAX = 2
|
|
+#endif
|
|
};
|
|
|
|
enum
|
|
diff -urN linux-2.6.11/include/net/addrconf.h x1/include/net/addrconf.h
|
|
--- linux-2.6.11/include/net/addrconf.h 2005-03-02 08:38:18.000000000 +0100
|
|
+++ x1/include/net/addrconf.h 2005-02-03 15:43:50.000000000 +0100
|
|
@@ -102,6 +102,8 @@
|
|
|
|
extern void addrconf_prefix_rcv(struct net_device *dev, u8 *opt, int len);
|
|
|
|
+extern int ipv6_get_hoplimit(struct net_device *dev);
|
|
+
|
|
/*
|
|
* anycast prototypes (anycast.c)
|
|
*/
|
|
diff -urN linux-2.6.11/include/net/dst.h x1/include/net/dst.h
|
|
--- linux-2.6.11/include/net/dst.h 2005-03-02 08:38:38.000000000 +0100
|
|
+++ x1/include/net/dst.h 2005-02-28 07:45:54.000000000 +0100
|
|
@@ -125,6 +125,18 @@
|
|
}
|
|
|
|
static inline int
|
|
+ip6_dst_allfrag(const struct dst_entry *dst)
|
|
+{
|
|
+#ifdef CONFIG_IPV6_ALLFRAG
|
|
+ int ret = dst_path_metric(dst, RTAX_FEATURES) & RTAX_FEATURE_ALLFRAG;
|
|
+ barrier();
|
|
+ return ret;
|
|
+#else
|
|
+ return 0;
|
|
+#endif
|
|
+};
|
|
+
|
|
+static inline int
|
|
dst_metric_locked(struct dst_entry *dst, int metric)
|
|
{
|
|
return dst_metric(dst, RTAX_LOCK) & (1<<metric);
|
|
diff -urN linux-2.6.11/include/net/if_inet6.h x1/include/net/if_inet6.h
|
|
--- linux-2.6.11/include/net/if_inet6.h 2005-03-02 08:38:32.000000000 +0100
|
|
+++ x1/include/net/if_inet6.h 2005-02-28 12:50:45.000000000 +0100
|
|
@@ -150,7 +150,13 @@
|
|
|
|
struct ipv6_devstat {
|
|
struct proc_dir_entry *proc_dir_entry;
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics);
|
|
+#endif
|
|
DEFINE_SNMP_STAT(struct icmpv6_mib, icmpv6);
|
|
+#if 0
|
|
+ DEFINE_SNMP_STAT(struct udp_mib, udp_stats_in6);
|
|
+#endif
|
|
};
|
|
|
|
struct inet6_dev
|
|
@@ -178,11 +184,15 @@
|
|
|
|
#ifdef CONFIG_IPV6_PRIVACY
|
|
u8 rndid[8];
|
|
+#ifdef CONFIG_IPV6_PRIVACY_MD5
|
|
u8 entropy[8];
|
|
+#endif
|
|
struct timer_list regen_timer;
|
|
struct inet6_ifaddr *tempaddr_list;
|
|
+#ifdef CONFIG_IPV6_PRIVACY_MD5
|
|
__u8 work_eui64[8];
|
|
__u8 work_digest[16];
|
|
+#endif /* CONFIG_IPV6_PRIVACY_MD5 */
|
|
#endif
|
|
|
|
struct neigh_parms *nd_parms;
|
|
diff -urN linux-2.6.11/include/net/ip6_route.h x1/include/net/ip6_route.h
|
|
--- linux-2.6.11/include/net/ip6_route.h 2005-03-02 08:37:50.000000000 +0100
|
|
+++ x1/include/net/ip6_route.h 2004-11-25 06:33:07.000000000 +0100
|
|
@@ -2,9 +2,9 @@
|
|
#define _NET_IP6_ROUTE_H
|
|
|
|
#define IP6_RT_PRIO_FW 16
|
|
-#define IP6_RT_PRIO_USER 1024
|
|
#define IP6_RT_PRIO_ADDRCONF 256
|
|
#define IP6_RT_PRIO_KERN 512
|
|
+#define IP6_RT_PRIO_USER 1024
|
|
#define IP6_RT_FLOW_MASK 0x00ff
|
|
|
|
#ifdef __KERNEL__
|
|
@@ -85,7 +85,8 @@
|
|
extern struct rt6_info * rt6_get_dflt_router(struct in6_addr *addr,
|
|
struct net_device *dev);
|
|
extern struct rt6_info * rt6_add_dflt_router(struct in6_addr *gwaddr,
|
|
- struct net_device *dev);
|
|
+ struct net_device *dev,
|
|
+ int pref);
|
|
|
|
extern void rt6_purge_dflt_routers(void);
|
|
|
|
diff -urN linux-2.6.11/include/net/ipv6.h x1/include/net/ipv6.h
|
|
--- linux-2.6.11/include/net/ipv6.h 2005-03-02 08:38:19.000000000 +0100
|
|
+++ x1/include/net/ipv6.h 2005-03-02 17:30:59.000000000 +0100
|
|
@@ -112,9 +112,30 @@
|
|
|
|
/* MIBs */
|
|
DECLARE_SNMP_STAT(struct ipstats_mib, ipv6_statistics);
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+#define IP6_INC_STATS(idev, field) ({ \
|
|
+ struct inet6_dev *_idev = (idev); \
|
|
+ if (likely(_idev != NULL)) \
|
|
+ SNMP_INC_STATS(idev->stats.ipv6_statistics, field); \
|
|
+ SNMP_INC_STATS(ipv6_statistics, field); \
|
|
+})
|
|
+#define IP6_INC_STATS_BH(idev, field) ({ \
|
|
+ struct inet6_dev *_idev = (idev); \
|
|
+ if (likely(_idev != NULL)) \
|
|
+ SNMP_INC_STATS_BH((_idev)->stats.ipv6_statistics, field); \
|
|
+ SNMP_INC_STATS_BH(ipv6_statistics, field); \
|
|
+})
|
|
+#define IP6_INC_STATS_USER(idev, field) ({ \
|
|
+ struct inet6_dev *_idev = (idev); \
|
|
+ if (likely(_idev != NULL)) \
|
|
+ SNMP_INC_STATS_USER(_idev->stats.ipv6_statistics, field); \
|
|
+ SNMP_INC_STATS_USER(ipv6_statistics, field); \
|
|
+})
|
|
+#else
|
|
#define IP6_INC_STATS(field) SNMP_INC_STATS(ipv6_statistics, field)
|
|
#define IP6_INC_STATS_BH(field) SNMP_INC_STATS_BH(ipv6_statistics, field)
|
|
#define IP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(ipv6_statistics, field)
|
|
+#endif
|
|
DECLARE_SNMP_STAT(struct icmpv6_mib, icmpv6_statistics);
|
|
#define ICMP6_INC_STATS(idev, field) ({ \
|
|
struct inet6_dev *_idev = (idev); \
|
|
@@ -142,9 +163,30 @@
|
|
SNMP_INC_STATS_OFFSET_BH(icmpv6_statistics, field, _offset); \
|
|
})
|
|
DECLARE_SNMP_STAT(struct udp_mib, udp_stats_in6);
|
|
+#if 0
|
|
+#define UDP6_INC_STATS(idev, field) ({ \
|
|
+ struct inet6_dev *_idev = (idev); \
|
|
+ if (likely(_idev != NULL)) \
|
|
+ SNMP_INC_STATS(idev->stats.udp_stats_in6, field); \
|
|
+ SNMP_INC_STATS(udp_stats_in6, field); \
|
|
+})
|
|
+#define UDP6_INC_STATS_BH(idev, field) ({ \
|
|
+ struct inet6_dev *_idev = (idev); \
|
|
+ if (likely(_idev != NULL)) \
|
|
+ SNMP_INC_STATS_BH((_idev)->stats.udp_stats_in6, field); \
|
|
+ SNMP_INC_STATS_BH(udp_stats_in6, field); \
|
|
+})
|
|
+#define UDP6_INC_STATS_USER(idev, field) ({ \
|
|
+ struct inet6_dev *_idev = (idev); \
|
|
+ if (likely(_idev != NULL)) \
|
|
+ SNMP_INC_STATS_USER(_idev->stats.udp_stats_in6, field); \
|
|
+ SNMP_INC_STATS_USER(udp_stats_in6, field); \
|
|
+})
|
|
+#else
|
|
#define UDP6_INC_STATS(field) SNMP_INC_STATS(udp_stats_in6, field)
|
|
#define UDP6_INC_STATS_BH(field) SNMP_INC_STATS_BH(udp_stats_in6, field)
|
|
-#define UDP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(udp_stats_in6, field)
|
|
+#define UDP6_INC_STATS_USER(field) SNMP_INC_STATS_USER(udp_stats_in6, field)
|
|
+#endif
|
|
extern atomic_t inet6_sock_nr;
|
|
|
|
int snmp6_register_dev(struct inet6_dev *idev);
|
|
@@ -231,7 +273,7 @@
|
|
void (*destructor)(struct sock *));
|
|
|
|
|
|
-extern int ipv6_parse_hopopts(struct sk_buff *skb, int);
|
|
+extern int ipv6_parse_hopopts(struct sk_buff **skb, unsigned int *nhoffp);
|
|
|
|
extern struct ipv6_txoptions * ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt);
|
|
|
|
@@ -249,12 +291,28 @@
|
|
char *,
|
|
unsigned int, unsigned int);
|
|
|
|
-
|
|
-extern int ipv6_addr_type(const struct in6_addr *addr);
|
|
+/*
|
|
+ * Address manipulation functions
|
|
+ */
|
|
+extern int __ipv6_addr_type(const struct in6_addr *addr);
|
|
+static inline int ipv6_addr_type(const struct in6_addr *addr)
|
|
+{
|
|
+ return __ipv6_addr_type(addr) & 0xffff;
|
|
+}
|
|
|
|
static inline int ipv6_addr_scope(const struct in6_addr *addr)
|
|
{
|
|
- return ipv6_addr_type(addr) & IPV6_ADDR_SCOPE_MASK;
|
|
+ return __ipv6_addr_type(addr) & IPV6_ADDR_SCOPE_MASK;
|
|
+}
|
|
+
|
|
+static inline int __ipv6_addr_src_scope(int type)
|
|
+{
|
|
+ return type == IPV6_ADDR_ANY ? __IPV6_ADDR_SCOPE_INVALID : type>>16;
|
|
+}
|
|
+
|
|
+static inline int ipv6_addr_src_scope(const struct in6_addr *addr)
|
|
+{
|
|
+ return __ipv6_addr_src_scope(__ipv6_addr_type(addr));
|
|
}
|
|
|
|
static inline int ipv6_addr_cmp(const struct in6_addr *a1, const struct in6_addr *a2)
|
|
@@ -305,6 +363,33 @@
|
|
a1->s6_addr32[3] == a2->s6_addr32[3]);
|
|
}
|
|
|
|
+/* compare "prefix length" bits of an address */
|
|
+static inline int __ipv6_prefix_equal(const u32 *a1, const u32 *a2,
|
|
+ unsigned int prefixlen)
|
|
+{
|
|
+ unsigned pdw, pbi;
|
|
+
|
|
+ /* check complete u32 in prefix */
|
|
+ pdw = prefixlen >> 5;
|
|
+ if (pdw && memcmp(a1, a2, pdw << 2))
|
|
+ return 0;
|
|
+
|
|
+ /* check incomplete u32 in prefix */
|
|
+ pbi = prefixlen & 0x1f;
|
|
+ if (pbi && ((a1[pdw] ^ a2[pdw]) & htonl((0xffffffff) << (32 - pbi))))
|
|
+ return 0;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static inline int ipv6_prefix_equal(const struct in6_addr *a1,
|
|
+ const struct in6_addr *a2,
|
|
+ unsigned int prefixlen)
|
|
+{
|
|
+ return __ipv6_prefix_equal(a1->s6_addr32, a2->s6_addr32,
|
|
+ prefixlen);
|
|
+}
|
|
+
|
|
static inline int ipv6_addr_any(const struct in6_addr *a)
|
|
{
|
|
return ((a->s6_addr32[0] | a->s6_addr32[1] |
|
|
@@ -368,6 +453,7 @@
|
|
extern int ip6_forward(struct sk_buff *skb);
|
|
extern int ip6_input(struct sk_buff *skb);
|
|
extern int ip6_mc_input(struct sk_buff *skb);
|
|
+extern int ip6_mr_input(struct sk_buff *skb);
|
|
|
|
/*
|
|
* Extension header (options) processing
|
|
diff -urN linux-2.6.11/include/net/neighbour.h x1/include/net/neighbour.h
|
|
--- linux-2.6.11/include/net/neighbour.h 2005-03-02 08:38:32.000000000 +0100
|
|
+++ x1/include/net/neighbour.h 2005-02-10 06:31:25.000000000 +0100
|
|
@@ -54,6 +54,8 @@
|
|
#include <linux/rcupdate.h>
|
|
#include <linux/seq_file.h>
|
|
|
|
+#include <linux/config.h>
|
|
+
|
|
#include <linux/err.h>
|
|
#include <linux/sysctl.h>
|
|
|
|
@@ -158,7 +160,8 @@
|
|
struct pneigh_entry
|
|
{
|
|
struct pneigh_entry *next;
|
|
- struct net_device *dev;
|
|
+ struct net_device *dev;
|
|
+ struct neigh_table *tbl;
|
|
u8 key[0];
|
|
};
|
|
|
|
@@ -205,6 +208,27 @@
|
|
#endif
|
|
};
|
|
|
|
+struct neigh_notifier_parms {
|
|
+ void (*link_notifier)(void *);
|
|
+ void *link_notifier_data;
|
|
+};
|
|
+
|
|
+static __inline__ char * neigh_state(int state)
|
|
+{
|
|
+ switch (state) {
|
|
+ case NUD_NONE: return "NONE";
|
|
+ case NUD_INCOMPLETE: return "INCOMPLETE";
|
|
+ case NUD_REACHABLE: return "REACHABLE";
|
|
+ case NUD_STALE: return "STALE";
|
|
+ case NUD_DELAY: return "DELAY";
|
|
+ case NUD_PROBE: return "PROBE";
|
|
+ case NUD_FAILED: return "FAILED";
|
|
+ case NUD_NOARP: return "NOARP";
|
|
+ case NUD_PERMANENT: return "PERMANENT";
|
|
+ default: return "???";
|
|
+ }
|
|
+}
|
|
+
|
|
/* flags for neigh_update() */
|
|
#define NEIGH_UPDATE_F_OVERRIDE 0x00000001
|
|
#define NEIGH_UPDATE_F_WEAK_OVERRIDE 0x00000002
|
|
@@ -274,7 +298,8 @@
|
|
struct neigh_parms *p,
|
|
int p_id, int pdev_id,
|
|
char *p_name,
|
|
- proc_handler *proc_handler);
|
|
+ proc_handler *proc_handler,
|
|
+ void (*notifier)(void *));
|
|
extern void neigh_sysctl_unregister(struct neigh_parms *p);
|
|
|
|
static inline void __neigh_parms_put(struct neigh_parms *parms)
|
|
@@ -300,18 +325,35 @@
|
|
|
|
static inline void neigh_release(struct neighbour *neigh)
|
|
{
|
|
+#ifdef CONFIG_IPV6_NDISC_DEBUG
|
|
+ printk(KERN_DEBUG "%s(neigh=%p): refcnt=%d\n",
|
|
+ __FUNCTION__, neigh, atomic_read(&neigh->refcnt)-1);
|
|
+#endif
|
|
if (atomic_dec_and_test(&neigh->refcnt))
|
|
neigh_destroy(neigh);
|
|
}
|
|
|
|
static inline struct neighbour * neigh_clone(struct neighbour *neigh)
|
|
{
|
|
+#ifdef CONFIG_IPV6_NDISC_DEBUG
|
|
+ printk(KERN_DEBUG "%s(neigh=%p): refcnt=%d\n",
|
|
+ __FUNCTION__, neigh, neigh ? atomic_read(&neigh->refcnt)+1 : 0);
|
|
+#endif
|
|
if (neigh)
|
|
atomic_inc(&neigh->refcnt);
|
|
return neigh;
|
|
}
|
|
|
|
+#ifdef CONFIG_IPV6_NDISC_DEBUG
|
|
+#define neigh_hold(n) ({ \
|
|
+ struct neighbour *_n = (n); \
|
|
+ printk(KERN_DEBUG "%s(neigh=%p): refcnt=%d\n", \
|
|
+ __FUNCTION__, _n, atomic_read(&_n->refcnt)+1); \
|
|
+ atomic_inc(&_n->refcnt); \
|
|
+})
|
|
+#else
|
|
#define neigh_hold(n) atomic_inc(&(n)->refcnt)
|
|
+#endif
|
|
|
|
static inline void neigh_confirm(struct neighbour *neigh)
|
|
{
|
|
@@ -331,6 +373,11 @@
|
|
|
|
static inline int neigh_event_send(struct neighbour *neigh, struct sk_buff *skb)
|
|
{
|
|
+#ifdef CONFIG_IPV6_NDISC_DEBUG
|
|
+ printk(KERN_DEBUG
|
|
+ "%s(neigh=%p, skb=%p): %s, refcnt=%d\n",
|
|
+ __FUNCTION__, neigh, skb, neigh_state(neigh->nud_state), atomic_read(&neigh->refcnt));
|
|
+#endif
|
|
neigh->used = jiffies;
|
|
if (!(neigh->nud_state&(NUD_CONNECTED|NUD_DELAY|NUD_PROBE)))
|
|
return __neigh_event_send(neigh, skb);
|
|
diff -urN linux-2.6.11/include/net/xfrm.h x1/include/net/xfrm.h
|
|
--- linux-2.6.11/include/net/xfrm.h 2005-03-02 08:38:25.000000000 +0100
|
|
+++ x1/include/net/xfrm.h 2005-02-03 13:45:59.000000000 +0100
|
|
@@ -471,8 +471,12 @@
|
|
static inline int
|
|
__xfrm6_selector_match(struct xfrm_selector *sel, struct flowi *fl)
|
|
{
|
|
- return addr_match(&fl->fl6_dst, &sel->daddr, sel->prefixlen_d) &&
|
|
- addr_match(&fl->fl6_src, &sel->saddr, sel->prefixlen_s) &&
|
|
+ return __ipv6_prefix_equal(fl->fl6_dst.s6_addr32,
|
|
+ sel->daddr.a6,
|
|
+ sel->prefixlen_d) &&
|
|
+ __ipv6_prefix_equal(fl->fl6_src.s6_addr32,
|
|
+ sel->saddr.a6,
|
|
+ sel->prefixlen_s) &&
|
|
!((xfrm_flowi_dport(fl) ^ sel->dport) & sel->dport_mask) &&
|
|
!((xfrm_flowi_sport(fl) ^ sel->sport) & sel->sport_mask) &&
|
|
(fl->proto == sel->proto || !sel->proto) &&
|
|
@@ -863,7 +867,7 @@
|
|
extern void km_policy_expired(struct xfrm_policy *pol, int dir, int hard);
|
|
|
|
extern void xfrm_input_init(void);
|
|
-extern int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq);
|
|
+extern int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi);
|
|
|
|
extern void xfrm_probe_algs(void);
|
|
extern int xfrm_count_auth_supported(void);
|
|
diff -urN linux-2.6.11/include/video/tgafb.h x1/include/video/tgafb.h
|
|
--- linux-2.6.11/include/video/tgafb.h 2005-03-02 08:38:19.000000000 +0100
|
|
+++ x1/include/video/tgafb.h 2004-12-07 11:12:39.000000000 +0100
|
|
@@ -47,7 +47,6 @@
|
|
#define TGA_VALID_REG 0x0070
|
|
#define TGA_CURSOR_XY_REG 0x0074
|
|
#define TGA_INTR_STAT_REG 0x007c
|
|
-#define TGA_DATA_REG 0x0080
|
|
#define TGA_RAMDAC_SETUP_REG 0x00c0
|
|
#define TGA_BLOCK_COLOR0_REG 0x0140
|
|
#define TGA_BLOCK_COLOR1_REG 0x0144
|
|
diff -urN linux-2.6.11/net/Kconfig x1/net/Kconfig
|
|
--- linux-2.6.11/net/Kconfig 2005-03-02 08:38:34.000000000 +0100
|
|
+++ x1/net/Kconfig 2005-02-28 12:50:45.000000000 +0100
|
|
@@ -81,6 +81,18 @@
|
|
|
|
Say Y unless you know what you are doing.
|
|
|
|
+config USE_POLICY_FWD
|
|
+ bool "Use xfrm policy fwd"
|
|
+ default y
|
|
+ ---help---
|
|
+ Using XFRM_POLICY_FWD which corespond to fwd in setkey
|
|
+ when specifing inbound forwarding policy.
|
|
+ This is USAGI original changes. If you want the original
|
|
+ kernel behavior of IPsec, say Y. If you want behavior
|
|
+ which is similar to KAME IPsec stack, say N.
|
|
+
|
|
+ Unsure, Say Y.
|
|
+
|
|
config INET
|
|
bool "TCP/IP networking"
|
|
---help---
|
|
@@ -107,12 +119,12 @@
|
|
|
|
# IPv6 as module will cause a CRASH if you try to unload it
|
|
config IPV6
|
|
- tristate "The IPv6 protocol (EXPERIMENTAL)"
|
|
- depends on INET && EXPERIMENTAL
|
|
- select CRYPTO if IPV6_PRIVACY
|
|
- select CRYPTO_MD5 if IPV6_PRIVACY
|
|
+ tristate "The IPv6 protocol"
|
|
+ depends on INET
|
|
+ select CRYPTO if IPV6_PRIVACY_MD5
|
|
+ select CRYPTO_MD5 if IPV6_PRIVACY_MD5
|
|
---help---
|
|
- This is experimental support for the IP version 6 (formerly called
|
|
+ This is additional support for the IP version 6 (formerly called
|
|
IPng "IP next generation"). You will still be able to do
|
|
regular IPv4 networking as well.
|
|
|
|
@@ -127,8 +139,6 @@
|
|
To compile this protocol support as a module, choose M here: the
|
|
module will be called ipv6.
|
|
|
|
- It is safe to say N here for now.
|
|
-
|
|
source "net/ipv6/Kconfig"
|
|
|
|
menuconfig NETFILTER
|
|
diff -urN linux-2.6.11/net/core/neighbour.c x1/net/core/neighbour.c
|
|
--- linux-2.6.11/net/core/neighbour.c 2005-03-02 08:37:47.000000000 +0100
|
|
+++ x1/net/core/neighbour.c 2005-02-20 04:42:00.000000000 +0100
|
|
@@ -33,13 +33,18 @@
|
|
#include <linux/rtnetlink.h>
|
|
#include <linux/random.h>
|
|
|
|
+#ifdef CONFIG_IPV6_NDISC_DEBUG
|
|
+#define NEIGH_DEBUG 3
|
|
+#else
|
|
#define NEIGH_DEBUG 1
|
|
+#endif
|
|
|
|
#define NEIGH_PRINTK(x...) printk(x)
|
|
#define NEIGH_NOPRINTK(x...) do { ; } while(0)
|
|
#define NEIGH_PRINTK0 NEIGH_PRINTK
|
|
#define NEIGH_PRINTK1 NEIGH_NOPRINTK
|
|
#define NEIGH_PRINTK2 NEIGH_NOPRINTK
|
|
+#define NEIGH_PRINTK3 NEIGH_NOPRINTK
|
|
|
|
#if NEIGH_DEBUG >= 1
|
|
#undef NEIGH_PRINTK1
|
|
@@ -49,6 +54,10 @@
|
|
#undef NEIGH_PRINTK2
|
|
#define NEIGH_PRINTK2 NEIGH_PRINTK
|
|
#endif
|
|
+#if NEIGH_DEBUG >= 3
|
|
+#undef NEIGH_PRINTK3
|
|
+#define NEIGH_PRINTK3 NEIGH_PRINTK
|
|
+#endif
|
|
|
|
#define PNEIGH_HASHMASK 0xF
|
|
|
|
@@ -118,6 +127,10 @@
|
|
int shrunk = 0;
|
|
int i;
|
|
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(tbl=%p)\n",
|
|
+ __FUNCTION__, tbl);
|
|
+
|
|
NEIGH_CACHE_STAT_INC(tbl, forced_gc_runs);
|
|
|
|
write_lock_bh(&tbl->lock);
|
|
@@ -156,9 +169,21 @@
|
|
{
|
|
if ((n->nud_state & NUD_IN_TIMER) &&
|
|
del_timer(&n->timer)) {
|
|
+
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(n=%p): %s, refcnt=%d\n",
|
|
+ __FUNCTION__,
|
|
+ n, neigh_state(n->nud_state), atomic_read(&n->refcnt) -1);
|
|
+
|
|
neigh_release(n);
|
|
return 1;
|
|
}
|
|
+
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(n=%p): %s, refcnt=%d\n",
|
|
+ __FUNCTION__,
|
|
+ n, neigh_state(n->nud_state), atomic_read(&n->refcnt));
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -203,6 +228,11 @@
|
|
{
|
|
int i;
|
|
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(tbl=%p, dev=%p)\n",
|
|
+ __FUNCTION__,
|
|
+ tbl, dev);
|
|
+
|
|
write_lock_bh(&tbl->lock);
|
|
|
|
for (i = 0; i <= tbl->hash_mask; i++) {
|
|
@@ -255,18 +285,30 @@
|
|
unsigned long now = jiffies;
|
|
int entries;
|
|
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(tbl=%p)\n",
|
|
+ __FUNCTION__, tbl);
|
|
+
|
|
entries = atomic_inc_return(&tbl->entries) - 1;
|
|
if (entries >= tbl->gc_thresh3 ||
|
|
(entries >= tbl->gc_thresh2 &&
|
|
time_after(now, tbl->last_flush + 5 * HZ))) {
|
|
if (!neigh_forced_gc(tbl) &&
|
|
- entries >= tbl->gc_thresh3)
|
|
+ entries > tbl->gc_thresh3) {
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(): failed to shrink table\n",
|
|
+ __FUNCTINO__);
|
|
goto out_entries;
|
|
+ }
|
|
}
|
|
|
|
n = kmem_cache_alloc(tbl->kmem_cachep, SLAB_ATOMIC);
|
|
- if (!n)
|
|
+ if (!n) {
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(): failed to allocate memory\n",
|
|
+ __FUNCTION__);
|
|
goto out_entries;
|
|
+ }
|
|
|
|
memset(n, 0, tbl->entry_size);
|
|
|
|
@@ -364,11 +406,20 @@
|
|
|
|
NEIGH_CACHE_STAT_INC(tbl, lookups);
|
|
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(tbl=%p, pkey=%p, dev=%p)\n",
|
|
+ __FUNCTION__,
|
|
+ tbl, pkey, dev);
|
|
+
|
|
read_lock_bh(&tbl->lock);
|
|
for (n = tbl->hash_buckets[hash_val]; n; n = n->next) {
|
|
if (dev == n->dev && !memcmp(n->primary_key, pkey, key_len)) {
|
|
neigh_hold(n);
|
|
NEIGH_CACHE_STAT_INC(tbl, hits);
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s() => %p (state=%s, refcnt=%d)\n",
|
|
+ __FUNCTION__,
|
|
+ n, neigh_state(n->nud_state), atomic_read(&n->refcnt));
|
|
break;
|
|
}
|
|
}
|
|
@@ -384,11 +435,20 @@
|
|
|
|
NEIGH_CACHE_STAT_INC(tbl, lookups);
|
|
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(tbl=%p, pkey=%p)\n",
|
|
+ __FUNCTION__,
|
|
+ tbl, pkey);
|
|
+
|
|
read_lock_bh(&tbl->lock);
|
|
for (n = tbl->hash_buckets[hash_val]; n; n = n->next) {
|
|
if (!memcmp(n->primary_key, pkey, key_len)) {
|
|
neigh_hold(n);
|
|
NEIGH_CACHE_STAT_INC(tbl, hits);
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s() => %p (state=%s, refcnt=%d)\n",
|
|
+ __FUNCTION__,
|
|
+ n, neigh_state(n->nud_state), atomic_read(&n->refcnt));
|
|
break;
|
|
}
|
|
}
|
|
@@ -402,7 +462,14 @@
|
|
u32 hash_val;
|
|
int key_len = tbl->key_len;
|
|
int error;
|
|
- struct neighbour *n1, *rc, *n = neigh_alloc(tbl);
|
|
+ struct neighbour *n1, *rc, *n;
|
|
+
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(tbl=%p, pkey=%p, dev=%p)\n",
|
|
+ __FUNCTION__,
|
|
+ tbl, pkey, dev);
|
|
+
|
|
+ n = neigh_alloc(tbl);
|
|
|
|
if (!n) {
|
|
rc = ERR_PTR(-ENOBUFS);
|
|
@@ -453,6 +520,10 @@
|
|
n->dead = 0;
|
|
neigh_hold(n);
|
|
write_unlock_bh(&tbl->lock);
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(): => %p (state=%s, refnt=%d)\n",
|
|
+ __FUNCTION__,
|
|
+ n, neigh_state(n->nud_state), atomic_read(&n->refcnt));
|
|
NEIGH_PRINTK2("neigh %p is created.\n", n);
|
|
rc = n;
|
|
out:
|
|
@@ -496,6 +567,7 @@
|
|
|
|
memcpy(n->key, pkey, key_len);
|
|
n->dev = dev;
|
|
+ n->tbl = tbl;
|
|
if (dev)
|
|
dev_hold(dev);
|
|
|
|
@@ -578,6 +650,11 @@
|
|
{
|
|
struct hh_cache *hh;
|
|
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(neigh=%p): %s, refcnt=%d, dead=%d\n",
|
|
+ __FUNCTION__,
|
|
+ neigh, neigh_state(neigh->nud_state), atomic_read(&neigh->refcnt), neigh->dead);
|
|
+
|
|
NEIGH_CACHE_STAT_INC(neigh->tbl, destroys);
|
|
|
|
if (!neigh->dead) {
|
|
@@ -623,6 +700,10 @@
|
|
{
|
|
struct hh_cache *hh;
|
|
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(neigh=%p): %s, refcnt=%d\n",
|
|
+ __FUNCTION__,
|
|
+ neigh, neigh_state(neigh->nud_state), atomic_read(&neigh->refcnt));
|
|
NEIGH_PRINTK2("neigh %p is suspected.\n", neigh);
|
|
|
|
neigh->output = neigh->ops->output;
|
|
@@ -640,6 +721,10 @@
|
|
{
|
|
struct hh_cache *hh;
|
|
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(neigh=%p): %s, refcnt=%d\n",
|
|
+ __FUNCTION__,
|
|
+ neigh, neigh_state(neigh->nud_state), atomic_read(&neigh->refcnt));
|
|
NEIGH_PRINTK2("neigh %p is connected.\n", neigh);
|
|
|
|
neigh->output = neigh->ops->connected_output;
|
|
@@ -654,6 +739,11 @@
|
|
struct neighbour *n, **np;
|
|
unsigned long expire, now = jiffies;
|
|
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(arg=%p)\n",
|
|
+ __FUNCTION__,
|
|
+ tbl);
|
|
+
|
|
NEIGH_CACHE_STAT_INC(tbl, periodic_gc_runs);
|
|
|
|
write_lock(&tbl->lock);
|
|
@@ -678,6 +768,11 @@
|
|
|
|
write_lock(&n->lock);
|
|
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(): - %p, state=%s, refcnt=%d\n",
|
|
+ __FUNCTION__,
|
|
+ n, neigh_state(n->nud_state), atomic_read(&n->refcnt));
|
|
+
|
|
state = n->nud_state;
|
|
if (state & (NUD_PERMANENT | NUD_IN_TIMER)) {
|
|
write_unlock(&n->lock);
|
|
@@ -733,17 +828,28 @@
|
|
struct neighbour *neigh = (struct neighbour *)arg;
|
|
unsigned state;
|
|
int notify = 0;
|
|
+ int refcnt;
|
|
|
|
write_lock(&neigh->lock);
|
|
-
|
|
state = neigh->nud_state;
|
|
+
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(arg=%p): %s, refcnt=%d\n",
|
|
+ __FUNCTION__,
|
|
+ neigh, neigh_state(neigh->nud_state), atomic_read(&neigh->refcnt));
|
|
+
|
|
now = jiffies;
|
|
next = now + HZ;
|
|
|
|
+
|
|
if (!(state & NUD_IN_TIMER)) {
|
|
#ifndef CONFIG_SMP
|
|
printk(KERN_WARNING "neigh: timer & !nud_in_timer\n");
|
|
#endif
|
|
+ refcnt = atomic_read(&neigh->refcnt) - 1;
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(): => state=%s, refcnt=%d\n",
|
|
+ __FUNCTION__, neigh_state(state), refcnt);
|
|
goto out;
|
|
}
|
|
|
|
@@ -788,6 +894,7 @@
|
|
neigh->nud_state = NUD_FAILED;
|
|
notify = 1;
|
|
NEIGH_CACHE_STAT_INC(neigh->tbl, res_failed);
|
|
+
|
|
NEIGH_PRINTK2("neigh %p is failed.\n", neigh);
|
|
|
|
/* It is very thin place. report_unreachable is very complicated
|
|
@@ -840,6 +947,11 @@
|
|
|
|
write_lock_bh(&neigh->lock);
|
|
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(neigh=%p, skb=%p): %s\n",
|
|
+ __FUNCTION__,
|
|
+ neigh, skb, neigh_state(neigh->nud_state));
|
|
+
|
|
rc = 0;
|
|
if (neigh->nud_state & (NUD_CONNECTED | NUD_DELAY | NUD_PROBE))
|
|
goto out_unlock_bh;
|
|
@@ -936,17 +1048,33 @@
|
|
struct net_device *dev;
|
|
int update_isrouter = 0;
|
|
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(neigh=%p, lladdr=%p, new=%u, flags=%08x): %s\n",
|
|
+ __FUNCTION__,
|
|
+ neigh, lladdr, new, flags, neigh_state(neigh->nud_state));
|
|
+
|
|
+ if (!neigh) {
|
|
+ NEIGH_PRINTK1(KERN_WARNING "neigh_update(): neigh==NULL\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
write_lock_bh(&neigh->lock);
|
|
|
|
dev = neigh->dev;
|
|
old = neigh->nud_state;
|
|
err = -EPERM;
|
|
|
|
+ if (!dev) {
|
|
+ NEIGH_PRINTK1(KERN_WARNING "neigh_update(): neigh->dev==NULL\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
if (!(flags & NEIGH_UPDATE_F_ADMIN) &&
|
|
(old & (NUD_NOARP | NUD_PERMANENT)))
|
|
goto out;
|
|
|
|
if (!(new & NUD_VALID)) {
|
|
+ /* NONE,INCOMPLETE,FAILED */
|
|
neigh_del_timer(neigh);
|
|
if (old & NUD_CONNECTED)
|
|
neigh_suspect(neigh);
|
|
@@ -1021,11 +1149,11 @@
|
|
}
|
|
|
|
if (lladdr != neigh->ha) {
|
|
+ neigh->updated = jiffies;
|
|
memcpy(&neigh->ha, lladdr, dev->addr_len);
|
|
neigh_update_hhs(neigh);
|
|
if (!(new & NUD_CONNECTED))
|
|
- neigh->confirmed = jiffies -
|
|
- (neigh->parms->base_reachable_time << 1);
|
|
+ neigh->confirmed = jiffies - (neigh->parms->base_reachable_time<<1);
|
|
#ifdef CONFIG_ARPD
|
|
notify = 1;
|
|
#endif
|
|
@@ -1040,7 +1168,6 @@
|
|
struct sk_buff *skb;
|
|
|
|
/* Again: avoid dead loop if something went wrong */
|
|
-
|
|
while (neigh->nud_state & NUD_VALID &&
|
|
(skb = __skb_dequeue(&neigh->arp_queue)) != NULL) {
|
|
struct neighbour *n1 = neigh;
|
|
@@ -1071,8 +1198,15 @@
|
|
u8 *lladdr, void *saddr,
|
|
struct net_device *dev)
|
|
{
|
|
- struct neighbour *neigh = __neigh_lookup(tbl, saddr, dev,
|
|
- lladdr || !dev->addr_len);
|
|
+ struct neighbour *neigh;
|
|
+
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(tbl=%p, lladdr=%p, saddr=%p, dev=%p)\n",
|
|
+ __FUNCTION__,
|
|
+ tbl, lladdr, saddr, dev);
|
|
+
|
|
+ neigh = __neigh_lookup(tbl, saddr, dev,
|
|
+ lladdr || !dev->addr_len);
|
|
if (neigh)
|
|
neigh_update(neigh, lladdr, NUD_STALE,
|
|
NEIGH_UPDATE_F_OVERRIDE);
|
|
@@ -1123,6 +1257,11 @@
|
|
{
|
|
struct net_device *dev = skb->dev;
|
|
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(skb=%p)\n",
|
|
+ __FUNCTION__,
|
|
+ skb);
|
|
+
|
|
__skb_pull(skb, skb->nh.raw - skb->data);
|
|
|
|
if (dev->hard_header &&
|
|
@@ -1142,6 +1281,11 @@
|
|
struct neighbour *neigh;
|
|
int rc = 0;
|
|
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(skb=%p)\n",
|
|
+ __FUNCTION__,
|
|
+ skb);
|
|
+
|
|
if (!dst || !(neigh = dst->neighbour))
|
|
goto discard;
|
|
|
|
@@ -1188,6 +1332,11 @@
|
|
struct neighbour *neigh = dst->neighbour;
|
|
struct net_device *dev = neigh->dev;
|
|
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(skb=%p)\n",
|
|
+ __FUNCTION__,
|
|
+ skb);
|
|
+
|
|
__skb_pull(skb, skb->nh.raw - skb->data);
|
|
|
|
read_lock_bh(&neigh->lock);
|
|
@@ -1424,6 +1573,11 @@
|
|
struct net_device *dev = NULL;
|
|
int err = -ENODEV;
|
|
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(skb=%p, nlh=%p, arg=%p)\n",
|
|
+ __FUNCTION__,
|
|
+ skb, nlh, arg);
|
|
+
|
|
if (ndm->ndm_ifindex &&
|
|
(dev = dev_get_by_index(ndm->ndm_ifindex)) == NULL)
|
|
goto out;
|
|
@@ -1476,6 +1630,11 @@
|
|
struct net_device *dev = NULL;
|
|
int err = -ENODEV;
|
|
|
|
+ NEIGH_PRINTK3(KERN_DEBUG
|
|
+ "%s(skb=%p, nlh=%p, arg=%p)\n",
|
|
+ __FUNCTION__,
|
|
+ skb, nlh, arg);
|
|
+
|
|
if (ndm->ndm_ifindex &&
|
|
(dev = dev_get_by_index(ndm->ndm_ifindex)) == NULL)
|
|
goto out;
|
|
@@ -1544,6 +1703,40 @@
|
|
return err;
|
|
}
|
|
|
|
+/*
|
|
+ * XXX: based on neigh_fill_info()
|
|
+ */
|
|
+static int pneigh_fill_info(struct sk_buff *skb, struct pneigh_entry *pn,
|
|
+ u32 pid, u32 seq, int event)
|
|
+{
|
|
+ int locked = 0;
|
|
+ unsigned char *b = skb->tail;
|
|
+ struct nlmsghdr *nlh = NLMSG_PUT(skb, pid, seq, event,
|
|
+ sizeof(struct ndmsg));
|
|
+ struct ndmsg *ndm = NLMSG_DATA(nlh);
|
|
+
|
|
+ read_lock_bh(&pn->tbl->lock);
|
|
+ locked = 1;
|
|
+ ndm->ndm_family = pn->tbl->family;
|
|
+ ndm->ndm_flags = NTF_PROXY;
|
|
+ ndm->ndm_type = 0;
|
|
+ ndm->ndm_ifindex = pn->dev->ifindex;
|
|
+ RTA_PUT(skb, NDA_DST, pn->tbl->key_len, pn->key);
|
|
+ read_unlock_bh(&pn->tbl->lock);
|
|
+ locked = 0;
|
|
+ ndm->ndm_state = 0;
|
|
+
|
|
+ nlh->nlmsg_len = skb->tail - b;
|
|
+ return skb->len;
|
|
+
|
|
+nlmsg_failure:
|
|
+rtattr_failure:
|
|
+ if (locked)
|
|
+ read_unlock_bh(&pn->tbl->lock);
|
|
+
|
|
+ skb_trim(skb, b - skb->data);
|
|
+ return -1;
|
|
+}
|
|
|
|
static int neigh_fill_info(struct sk_buff *skb, struct neighbour *n,
|
|
u32 pid, u32 seq, int event)
|
|
@@ -1585,6 +1778,42 @@
|
|
return -1;
|
|
}
|
|
|
|
+/*
|
|
+ * XXX: based on neigh_dump_table()
|
|
+ */
|
|
+static int pneigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
|
|
+ struct netlink_callback *cb)
|
|
+{
|
|
+ struct pneigh_entry *pn;
|
|
+ int rc, h, s_h = cb->args[1];
|
|
+ int idx, s_idx = idx = cb->args[2];
|
|
+
|
|
+ for (h = 0; h <= PNEIGH_HASHMASK; h++) {
|
|
+ if (h < s_h)
|
|
+ continue;
|
|
+ if (h > s_h)
|
|
+ s_idx = 0;
|
|
+ read_lock_bh(&tbl->lock);
|
|
+ for (pn = tbl->phash_buckets[h], idx = 0; pn; pn = pn->next, idx++) {
|
|
+ if (idx < s_idx)
|
|
+ continue;
|
|
+ if (pneigh_fill_info(skb, pn, NETLINK_CB(cb->skb).pid,
|
|
+ cb->nlh->nlmsg_seq,
|
|
+ RTM_NEWNEIGH) <= 0) {
|
|
+ read_unlock_bh(&tbl->lock);
|
|
+ rc = -1;
|
|
+ goto out;
|
|
+ }
|
|
+ }
|
|
+ read_unlock_bh(&tbl->lock);
|
|
+ }
|
|
+
|
|
+ rc = skb->len;
|
|
+out:
|
|
+ cb->args[1] = h;
|
|
+ cb->args[2] = idx;
|
|
+ return rc;
|
|
+}
|
|
|
|
static int neigh_dump_table(struct neigh_table *tbl, struct sk_buff *skb,
|
|
struct netlink_callback *cb)
|
|
@@ -1623,10 +1852,15 @@
|
|
{
|
|
struct neigh_table *tbl;
|
|
int t, family, s_t;
|
|
+ long a1, a2, pa1, pa2;
|
|
|
|
read_lock(&neigh_tbl_lock);
|
|
family = ((struct rtgenmsg *)NLMSG_DATA(cb->nlh))->rtgen_family;
|
|
s_t = cb->args[0];
|
|
+ a1 = cb->args[1];
|
|
+ a2 = cb->args[2];
|
|
+ pa1 = cb->args[1];
|
|
+ pa2 = cb->args[2];
|
|
|
|
for (tbl = neigh_tables, t = 0; tbl; tbl = tbl->next, t++) {
|
|
if (t < s_t || (family && tbl->family != family))
|
|
@@ -1634,12 +1868,28 @@
|
|
if (t > s_t)
|
|
memset(&cb->args[1], 0, sizeof(cb->args) -
|
|
sizeof(cb->args[0]));
|
|
+ cb->args[1] = a1;
|
|
+ cb->args[2] = a2;
|
|
if (neigh_dump_table(tbl, skb, cb) < 0)
|
|
break;
|
|
+ a1 = cb->args[1];
|
|
+ a2 = cb->args[2];
|
|
+
|
|
+ cb->args[1] = pa1;
|
|
+ cb->args[2] = pa2;
|
|
+ if (pneigh_dump_table(tbl, skb, cb) < 0)
|
|
+ break;
|
|
+ pa1 = cb->args[1];
|
|
+ pa2 = cb->args[2];
|
|
}
|
|
read_unlock(&neigh_tbl_lock);
|
|
|
|
cb->args[0] = t;
|
|
+ /*
|
|
+ * XXX: Fix me! currently only neigh's status is reported.
|
|
+ */
|
|
+ cb->args[1] = a1;
|
|
+ cb->args[2] = a2;
|
|
return skb->len;
|
|
}
|
|
|
|
@@ -2044,14 +2294,80 @@
|
|
#endif /* CONFIG_ARPD */
|
|
|
|
#ifdef CONFIG_SYSCTL
|
|
+static int
|
|
+ndisc_proc_dointvec_ms_jiffies(struct ctl_table *ctl,
|
|
+ int write,
|
|
+ struct file *filp,
|
|
+ void __user *buffer, size_t *lenp,
|
|
+ loff_t *ppos)
|
|
+{
|
|
+ struct neigh_notifier_parms *np = ctl->extra1;
|
|
+ int ret = proc_dointvec_ms_jiffies(ctl, write, filp, buffer, lenp, ppos);
|
|
+
|
|
+ if (write && np && np->link_notifier)
|
|
+ (np->link_notifier)(np->link_notifier_data);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int
|
|
+ndisc_proc_rtime_dointvec_ms_jiffies(struct ctl_table *ctl,
|
|
+ int write,
|
|
+ struct file *filp,
|
|
+ void __user *buffer, size_t *lenp,
|
|
+ loff_t *ppos)
|
|
+{
|
|
+ struct neigh_notifier_parms *np = ctl->extra1;
|
|
+ int ret = proc_dointvec_ms_jiffies(ctl, write, filp, buffer, lenp, ppos);
|
|
+
|
|
+ if (write) {
|
|
+ if (ctl->extra2)
|
|
+ *((unsigned int *)ctl->extra2) = neigh_rand_reach_time(*(unsigned int *)ctl->data);
|
|
+ if (np && np->link_notifier)
|
|
+ (np->link_notifier)(np->link_notifier_data);
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int
|
|
+ndisc_sysctl_ms_jiffies(ctl_table *ctl, int __user *name, int nlen,
|
|
+ void __user *oldval, size_t __user *oldlenp,
|
|
+ void __user *newval, size_t newlen, void **context)
|
|
+{
|
|
+ struct neigh_notifier_parms *np = ctl->extra1;
|
|
+ int ret = sysctl_ms_jiffies(ctl, name, nlen,
|
|
+ oldval, oldlenp, newval, newlen,
|
|
+ context);
|
|
+ if (newval && newlen && ret > 0 && np && np->link_notifier)
|
|
+ (np->link_notifier)(np->link_notifier_data);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int
|
|
+ndisc_rtime_sysctl_ms_jiffies(ctl_table *ctl, int __user *name, int nlen,
|
|
+ void __user *oldval, size_t __user *oldlenp,
|
|
+ void __user *newval, size_t newlen, void **context)
|
|
+{
|
|
+ struct neigh_notifier_parms *np = ctl->extra1;
|
|
+ int ret = sysctl_ms_jiffies(ctl, name, nlen,
|
|
+ oldval, oldlenp, newval, newlen,
|
|
+ context);
|
|
+ if (newval && newlen && ret > 0 && np && np->link_notifier) {
|
|
+ if (ctl->extra2)
|
|
+ *((unsigned int *)ctl->extra2) = neigh_rand_reach_time(*(unsigned int *)ctl->data);
|
|
+ if (np && np->link_notifier)
|
|
+ (np->link_notifier)(np->link_notifier_data);
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
|
|
static struct neigh_sysctl_table {
|
|
struct ctl_table_header *sysctl_header;
|
|
- ctl_table neigh_vars[17];
|
|
+ ctl_table neigh_vars[__NET_NEIGH_MAX];
|
|
ctl_table neigh_dev[2];
|
|
ctl_table neigh_neigh_dir[2];
|
|
ctl_table neigh_proto_dir[2];
|
|
ctl_table neigh_root_dir[2];
|
|
+ struct neigh_notifier_parms notifier;
|
|
} neigh_sysctl_template = {
|
|
.neigh_vars = {
|
|
{
|
|
@@ -2170,6 +2486,23 @@
|
|
.mode = 0644,
|
|
.proc_handler = &proc_dointvec,
|
|
},
|
|
+ {
|
|
+ .ctl_name = NET_NEIGH_RETRANS_TIME_MS,
|
|
+ .procname = "retrans_time_ms",
|
|
+ .maxlen = sizeof(int),
|
|
+ .mode = 0644,
|
|
+ .proc_handler = &ndisc_proc_dointvec_ms_jiffies,
|
|
+ .strategy = &ndisc_sysctl_ms_jiffies,
|
|
+ },
|
|
+ {
|
|
+ .ctl_name = NET_NEIGH_REACHABLE_TIME_MS,
|
|
+ .procname = "base_reachable_time_ms",
|
|
+ .maxlen = sizeof(int),
|
|
+ .mode = 0644,
|
|
+ .proc_handler = &ndisc_proc_rtime_dointvec_ms_jiffies,
|
|
+ .strategy = &ndisc_rtime_sysctl_ms_jiffies,
|
|
+ },
|
|
+
|
|
},
|
|
.neigh_dev = {
|
|
{
|
|
@@ -2200,7 +2533,8 @@
|
|
|
|
int neigh_sysctl_register(struct net_device *dev, struct neigh_parms *p,
|
|
int p_id, int pdev_id, char *p_name,
|
|
- proc_handler *handler)
|
|
+ proc_handler *handler,
|
|
+ void (*neigh_notifier)(void *))
|
|
{
|
|
struct neigh_sysctl_table *t = kmalloc(sizeof(*t), GFP_KERNEL);
|
|
const char *dev_name_source = NULL;
|
|
@@ -2210,6 +2544,10 @@
|
|
if (!t)
|
|
return -ENOBUFS;
|
|
memcpy(t, &neigh_sysctl_template, sizeof(*t));
|
|
+
|
|
+ t->notifier.link_notifier = neigh_notifier;
|
|
+ t->notifier.link_notifier_data = dev;
|
|
+
|
|
t->neigh_vars[0].data = &p->mcast_probes;
|
|
t->neigh_vars[1].data = &p->ucast_probes;
|
|
t->neigh_vars[2].data = &p->app_probes;
|
|
@@ -2231,7 +2569,10 @@
|
|
if (dev) {
|
|
dev_name_source = dev->name;
|
|
t->neigh_dev[0].ctl_name = dev->ifindex;
|
|
- memset(&t->neigh_vars[12], 0, sizeof(ctl_table));
|
|
+ t->neigh_vars[12].procname = NULL;
|
|
+ t->neigh_vars[13].procname = NULL;
|
|
+ t->neigh_vars[14].procname = NULL;
|
|
+ t->neigh_vars[15].procname = NULL;
|
|
} else {
|
|
t->neigh_vars[12].data = (int *)(p + 1);
|
|
t->neigh_vars[13].data = (int *)(p + 1) + 1;
|
|
@@ -2239,6 +2580,13 @@
|
|
t->neigh_vars[15].data = (int *)(p + 1) + 3;
|
|
}
|
|
|
|
+ t->neigh_vars[16].data = &p->retrans_time;
|
|
+ t->neigh_vars[16].extra1 = &t->notifier;
|
|
+
|
|
+ t->neigh_vars[17].data = &p->base_reachable_time;
|
|
+ t->neigh_vars[17].extra1 = &t->notifier;
|
|
+ t->neigh_vars[17].extra2 = &p->reachable_time;
|
|
+
|
|
dev_name = net_sysctl_strdup(dev_name_source);
|
|
if (!dev_name) {
|
|
err = -ENOBUFS;
|
|
@@ -2313,6 +2661,7 @@
|
|
|
|
#ifdef CONFIG_ARPD
|
|
EXPORT_SYMBOL(neigh_app_ns);
|
|
+EXPORT_SYMBOL(neigh_app_notify);
|
|
#endif
|
|
#ifdef CONFIG_SYSCTL
|
|
EXPORT_SYMBOL(neigh_sysctl_register);
|
|
diff -urN linux-2.6.11/net/core/netfilter.c x1/net/core/netfilter.c
|
|
--- linux-2.6.11/net/core/netfilter.c 2005-03-02 08:38:08.000000000 +0100
|
|
+++ x1/net/core/netfilter.c 2005-02-03 05:44:13.000000000 +0100
|
|
@@ -806,6 +806,9 @@
|
|
tracking in use: without this, connection may not be in hash table, and hence
|
|
manufactured ICMP or RST packets will not be associated with it. */
|
|
void (*ip_ct_attach)(struct sk_buff *, struct sk_buff *);
|
|
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
|
+void (*ip6_ct_attach)(struct sk_buff *, struct sk_buff *);
|
|
+#endif
|
|
|
|
void nf_ct_attach(struct sk_buff *new, struct sk_buff *skb)
|
|
{
|
|
@@ -828,6 +831,9 @@
|
|
}
|
|
|
|
EXPORT_SYMBOL(ip_ct_attach);
|
|
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
|
+EXPORT_SYMBOL(ip6_ct_attach);
|
|
+#endif
|
|
EXPORT_SYMBOL(nf_ct_attach);
|
|
EXPORT_SYMBOL(nf_getsockopt);
|
|
EXPORT_SYMBOL(nf_hook_slow);
|
|
diff -urN linux-2.6.11/net/ipv4/ah4.c x1/net/ipv4/ah4.c
|
|
--- linux-2.6.11/net/ipv4/ah4.c 2005-03-02 08:38:25.000000000 +0100
|
|
+++ x1/net/ipv4/ah4.c 2005-02-03 06:35:55.000000000 +0100
|
|
@@ -128,6 +128,8 @@
|
|
goto out;
|
|
|
|
ah = (struct ip_auth_hdr*)skb->data;
|
|
+ if (x->props.replay_window && xfrm_replay_check(x, ah->seq_no))
|
|
+ goto out;
|
|
ahp = x->data;
|
|
ah_hlen = (ah->hdrlen + 2) << 2;
|
|
|
|
@@ -171,6 +173,8 @@
|
|
goto out;
|
|
}
|
|
}
|
|
+ if (x->props.replay_window)
|
|
+ xfrm_replay_advance(x, ah->seq_no);
|
|
((struct iphdr*)work_buf)->protocol = ah->nexthdr;
|
|
skb->nh.raw = skb_pull(skb, ah_hlen);
|
|
memcpy(skb->nh.raw, work_buf, iph->ihl*4);
|
|
diff -urN linux-2.6.11/net/ipv4/arp.c x1/net/ipv4/arp.c
|
|
--- linux-2.6.11/net/ipv4/arp.c 2005-03-02 08:38:25.000000000 +0100
|
|
+++ x1/net/ipv4/arp.c 2005-02-10 06:31:25.000000000 +0100
|
|
@@ -1243,7 +1243,7 @@
|
|
arp_proc_init();
|
|
#ifdef CONFIG_SYSCTL
|
|
neigh_sysctl_register(NULL, &arp_tbl.parms, NET_IPV4,
|
|
- NET_IPV4_NEIGH, "ipv4", NULL);
|
|
+ NET_IPV4_NEIGH, "ipv4", NULL, NULL);
|
|
#endif
|
|
register_netdevice_notifier(&arp_netdev_notifier);
|
|
}
|
|
diff -urN linux-2.6.11/net/ipv4/devinet.c x1/net/ipv4/devinet.c
|
|
--- linux-2.6.11/net/ipv4/devinet.c 2005-03-02 08:37:50.000000000 +0100
|
|
+++ x1/net/ipv4/devinet.c 2005-02-10 06:31:25.000000000 +0100
|
|
@@ -153,7 +153,7 @@
|
|
dev_hold(dev);
|
|
#ifdef CONFIG_SYSCTL
|
|
neigh_sysctl_register(dev, in_dev->arp_parms, NET_IPV4,
|
|
- NET_IPV4_NEIGH, "ipv4", NULL);
|
|
+ NET_IPV4_NEIGH, "ipv4", NULL, NULL);
|
|
#endif
|
|
|
|
/* Account for reference dev->ip_ptr */
|
|
@@ -992,7 +992,7 @@
|
|
devinet_sysctl_unregister(&in_dev->cnf);
|
|
neigh_sysctl_unregister(in_dev->arp_parms);
|
|
neigh_sysctl_register(dev, in_dev->arp_parms, NET_IPV4,
|
|
- NET_IPV4_NEIGH, "ipv4", NULL);
|
|
+ NET_IPV4_NEIGH, "ipv4", NULL, NULL);
|
|
devinet_sysctl_register(in_dev, &in_dev->cnf);
|
|
#endif
|
|
break;
|
|
diff -urN linux-2.6.11/net/ipv4/esp4.c x1/net/ipv4/esp4.c
|
|
--- linux-2.6.11/net/ipv4/esp4.c 2005-03-02 08:38:10.000000000 +0100
|
|
+++ x1/net/ipv4/esp4.c 2005-02-03 06:35:55.000000000 +0100
|
|
@@ -153,6 +153,7 @@
|
|
|
|
if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr)))
|
|
goto out;
|
|
+ esph = (struct ip_esp_hdr*)skb->data;
|
|
|
|
if (elen <= 0 || (elen & (blksize-1)))
|
|
goto out;
|
|
@@ -161,7 +162,8 @@
|
|
if (esp->auth.icv_full_len) {
|
|
u8 sum[esp->auth.icv_full_len];
|
|
u8 sum1[alen];
|
|
-
|
|
+ if (x->props.replay_window && xfrm_replay_check(x, esph->seq_no))
|
|
+ goto out;
|
|
esp->auth.icv(esp, skb, 0, skb->len-alen, sum);
|
|
|
|
if (skb_copy_bits(skb, skb->len-alen, sum1, alen))
|
|
@@ -171,6 +173,9 @@
|
|
x->stats.integrity_failed++;
|
|
goto out;
|
|
}
|
|
+
|
|
+ if (x->props.replay_window)
|
|
+ xfrm_replay_advance(x, esph->seq_no);
|
|
}
|
|
|
|
if ((nfrags = skb_cow_data(skb, 0, &trailer)) < 0)
|
|
@@ -178,7 +183,6 @@
|
|
|
|
skb->ip_summed = CHECKSUM_NONE;
|
|
|
|
- esph = (struct ip_esp_hdr*)skb->data;
|
|
iph = skb->nh.iph;
|
|
|
|
/* Get ivec. This can be wrong, check against another impls. */
|
|
@@ -373,7 +377,7 @@
|
|
if (x->aalg->alg_key_len > 512)
|
|
goto error;
|
|
}
|
|
- if (x->ealg == NULL)
|
|
+ if (x->ealg == NULL || (x->ealg->alg_key_len == 0 && x->props.ealgo != SADB_EALG_NULL))
|
|
goto error;
|
|
|
|
esp = kmalloc(sizeof(*esp), GFP_KERNEL);
|
|
@@ -412,11 +416,13 @@
|
|
goto error;
|
|
}
|
|
esp->conf.key = x->ealg->alg_key;
|
|
- esp->conf.key_len = (x->ealg->alg_key_len+7)/8;
|
|
- if (x->props.ealgo == SADB_EALG_NULL)
|
|
+ if (x->props.ealgo == SADB_EALG_NULL) {
|
|
+ esp->conf.key_len = 0;
|
|
esp->conf.tfm = crypto_alloc_tfm(x->ealg->alg_name, CRYPTO_TFM_MODE_ECB);
|
|
- else
|
|
+ } else {
|
|
+ esp->conf.key_len = (x->ealg->alg_key_len+7)/8;
|
|
esp->conf.tfm = crypto_alloc_tfm(x->ealg->alg_name, CRYPTO_TFM_MODE_CBC);
|
|
+ }
|
|
if (esp->conf.tfm == NULL)
|
|
goto error;
|
|
esp->conf.ivlen = crypto_tfm_alg_ivsize(esp->conf.tfm);
|
|
diff -urN linux-2.6.11/net/ipv4/ip_forward.c x1/net/ipv4/ip_forward.c
|
|
--- linux-2.6.11/net/ipv4/ip_forward.c 2005-03-02 08:37:30.000000000 +0100
|
|
+++ x1/net/ipv4/ip_forward.c 2005-01-21 06:15:37.000000000 +0100
|
|
@@ -60,8 +60,13 @@
|
|
struct rtable *rt; /* Route we use */
|
|
struct ip_options * opt = &(IPCB(skb)->opt);
|
|
|
|
+#ifdef CONFIG_USE_POLICY_FWD
|
|
if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb))
|
|
goto drop;
|
|
+#else
|
|
+ if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb))
|
|
+ goto drop;
|
|
+#endif
|
|
|
|
if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
|
|
return NET_RX_SUCCESS;
|
|
diff -urN linux-2.6.11/net/ipv4/xfrm4_input.c x1/net/ipv4/xfrm4_input.c
|
|
--- linux-2.6.11/net/ipv4/xfrm4_input.c 2005-03-02 08:37:48.000000000 +0100
|
|
+++ x1/net/ipv4/xfrm4_input.c 2004-11-25 06:33:09.000000000 +0100
|
|
@@ -31,30 +31,29 @@
|
|
IP_ECN_set_ce(inner_iph);
|
|
}
|
|
|
|
-static int xfrm4_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq)
|
|
+static int xfrm4_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi)
|
|
{
|
|
switch (nexthdr) {
|
|
case IPPROTO_IPIP:
|
|
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
|
|
return -EINVAL;
|
|
*spi = skb->nh.iph->saddr;
|
|
- *seq = 0;
|
|
return 0;
|
|
}
|
|
|
|
- return xfrm_parse_spi(skb, nexthdr, spi, seq);
|
|
+ return xfrm_parse_spi(skb, nexthdr, spi);
|
|
}
|
|
|
|
int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type)
|
|
{
|
|
int err;
|
|
- u32 spi, seq;
|
|
+ u32 spi;
|
|
struct sec_decap_state xfrm_vec[XFRM_MAX_DEPTH];
|
|
struct xfrm_state *x;
|
|
int xfrm_nr = 0;
|
|
int decaps = 0;
|
|
|
|
- if ((err = xfrm4_parse_spi(skb, skb->nh.iph->protocol, &spi, &seq)) != 0)
|
|
+ if ((err = xfrm4_parse_spi(skb, skb->nh.iph->protocol, &spi)) != 0)
|
|
goto drop;
|
|
|
|
do {
|
|
@@ -71,9 +70,6 @@
|
|
if (unlikely(x->km.state != XFRM_STATE_VALID))
|
|
goto drop_unlock;
|
|
|
|
- if (x->props.replay_window && xfrm_replay_check(x, seq))
|
|
- goto drop_unlock;
|
|
-
|
|
if (xfrm_state_check_expire(x))
|
|
goto drop_unlock;
|
|
|
|
@@ -84,9 +80,6 @@
|
|
/* only the first xfrm gets the encap type */
|
|
encap_type = 0;
|
|
|
|
- if (x->props.replay_window)
|
|
- xfrm_replay_advance(x, seq);
|
|
-
|
|
x->curlft.bytes += skb->len;
|
|
x->curlft.packets++;
|
|
|
|
@@ -116,8 +109,9 @@
|
|
break;
|
|
}
|
|
|
|
- if ((err = xfrm_parse_spi(skb, skb->nh.iph->protocol, &spi, &seq)) < 0)
|
|
+ if ((err = xfrm_parse_spi(skb, skb->nh.iph->protocol, &spi)) < 0)
|
|
goto drop;
|
|
+
|
|
} while (!err);
|
|
|
|
/* Allocate new secpath or COW existing one. */
|
|
diff -urN linux-2.6.11/net/ipv6/Kconfig x1/net/ipv6/Kconfig
|
|
--- linux-2.6.11/net/ipv6/Kconfig 2005-03-02 08:38:09.000000000 +0100
|
|
+++ x1/net/ipv6/Kconfig 2005-02-28 12:50:45.000000000 +0100
|
|
@@ -2,7 +2,7 @@
|
|
# IPv6 configuration
|
|
#
|
|
config IPV6_PRIVACY
|
|
- bool "IPv6: Privacy Extensions (RFC 3041) support"
|
|
+ bool "IPv6: Privacy Extensions support"
|
|
depends on IPV6
|
|
---help---
|
|
Privacy Extensions for Stateless Address Autoconfiguration in IPv6
|
|
@@ -17,6 +17,22 @@
|
|
|
|
See <file:Documentation/networking/ip-sysctl.txt> for details.
|
|
|
|
+config IPV6_PRIVACY_MD5
|
|
+ bool "IPv6: Use RFC 3041 randomized interface identifiers"
|
|
+ depends on IPV6_PRIVACY
|
|
+ ---help---
|
|
+ Instead of standard pseudo random generator, use
|
|
+ traditional algorithm described in the original RFC 3041.
|
|
+
|
|
+config IPV6_ROUTER_PREF
|
|
+ bool "IPv6: default router preference"
|
|
+ depends on IPV6
|
|
+
|
|
+config IPV6_NEW_ROUNDROBIN
|
|
+ bool
|
|
+ depends on IPV6_ROUTER_PREF
|
|
+ default y
|
|
+
|
|
config INET6_AH
|
|
tristate "IPv6: AH transformation"
|
|
depends on IPV6
|
|
@@ -77,3 +93,51 @@
|
|
|
|
If unsure, say N.
|
|
|
|
+config IPV6_MROUTE
|
|
+ bool "IPv6: multicast routing (EXPERIMENTAL)"
|
|
+ depends on IPV6 && EXPERIMENTAL
|
|
+ ---help---
|
|
+ Experimental support for IPv6 multicast forwarding.
|
|
+ If unsure, say N.
|
|
+
|
|
+config IPV6_PIMSM_V2
|
|
+ bool "IPv6: PIM-SM version 2 support (EXPERIMENTAL)"
|
|
+ depends on IPV6_MROUTE
|
|
+ ---help---
|
|
+ Support for IPv6 PIM multicast routing protocol PIM-SMv2.
|
|
+ If unsure, say N.
|
|
+
|
|
+config IPV6_STATISTICS
|
|
+ bool "IPv6: per-interface statistics for SNMP"
|
|
+ depends on IPV6
|
|
+
|
|
+config IPV6_DELPREFIX
|
|
+ bool "IPv6: delete prefix route on manual address deletion"
|
|
+ depends on IPV6
|
|
+ ---help---
|
|
+ When an address is being configured manually, kernel
|
|
+ automatically append a prefix route for the address.
|
|
+ On the other hand, kernel won't delete it when the address
|
|
+ is being deleted.
|
|
+
|
|
+ It is not a problem on prefix for auto-configured address,
|
|
+ but this probably is confusing on prefix for manual addresses.
|
|
+ (Note: prefix route for auto-configured address is managed
|
|
+ by its timer.)
|
|
+
|
|
+ With this option, delete prefix route if there's no address
|
|
+ on the device. In addition, if all the other address are
|
|
+ auto-configured address, we change it to dynamic
|
|
+ prefix route.
|
|
+
|
|
+ This is experimental.
|
|
+
|
|
+config IPV6_ALLFRAG
|
|
+ bool "IPv6: fragment < 1280"
|
|
+ depends on IPV6
|
|
+ ---help---
|
|
+ According to RFC2460, always append fragment header after
|
|
+ receiving TooBig w/ mtu < 1280.
|
|
+
|
|
+ This is experimental.
|
|
+
|
|
diff -urN linux-2.6.11/net/ipv6/Makefile x1/net/ipv6/Makefile
|
|
--- linux-2.6.11/net/ipv6/Makefile 2005-03-02 08:38:17.000000000 +0100
|
|
+++ x1/net/ipv6/Makefile 2005-02-09 16:31:39.000000000 +0100
|
|
@@ -10,6 +10,8 @@
|
|
exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \
|
|
ip6_flowlabel.o ipv6_syms.o
|
|
|
|
+ipv6-$(CONFIG_IPV6_MROUTE) += ip6mr.o
|
|
+
|
|
ipv6-$(CONFIG_XFRM) += xfrm6_policy.o xfrm6_state.o xfrm6_input.o \
|
|
xfrm6_output.o
|
|
ipv6-objs += $(ipv6-y)
|
|
diff -urN linux-2.6.11/net/ipv6/addrconf.c x1/net/ipv6/addrconf.c
|
|
--- linux-2.6.11/net/ipv6/addrconf.c 2005-03-02 08:38:26.000000000 +0100
|
|
+++ x1/net/ipv6/addrconf.c 2005-03-02 17:30:59.000000000 +0100
|
|
@@ -35,6 +35,10 @@
|
|
* YOSHIFUJI Hideaki @USAGI : ARCnet support
|
|
* YOSHIFUJI Hideaki @USAGI : convert /proc/net/if_inet6 to
|
|
* seq_file.
|
|
+ * YOSHIFUJI Hideaki @USAGI : improved source address
|
|
+ * selection; consider scope,
|
|
+ * status etc.
|
|
+ * Hoerdt Mickael : Added Multicast routing support.
|
|
*/
|
|
|
|
#include <linux/config.h>
|
|
@@ -105,8 +109,10 @@
|
|
static void ipv6_regen_rndid(unsigned long data);
|
|
|
|
static int desync_factor = MAX_DESYNC_FACTOR * HZ;
|
|
+#ifdef CONFIG_IPV6_PRIVACY_MD5
|
|
static struct crypto_tfm *md5_tfm;
|
|
static DEFINE_SPINLOCK(md5_tfm_lock);
|
|
+#endif /* CONFIG_IPV6_PRIVACY_MD5 */
|
|
#endif
|
|
|
|
static int ipv6_count_addresses(struct inet6_dev *idev);
|
|
@@ -144,6 +150,8 @@
|
|
|
|
static struct notifier_block *inet6addr_chain;
|
|
|
|
+static u32 ipv6_addrselect_label_lookup(const struct in6_addr *addr, int ifindex);
|
|
+
|
|
struct ipv6_devconf ipv6_devconf = {
|
|
.forwarding = 0,
|
|
.hop_limit = IPV6_DEFAULT_HOPLIMIT,
|
|
@@ -164,6 +172,9 @@
|
|
.max_desync_factor = MAX_DESYNC_FACTOR,
|
|
#endif
|
|
.max_addresses = IPV6_MAX_ADDRESSES,
|
|
+#ifdef CONFIG_IPV6_MROUTE
|
|
+ .mc_forwarding = 0,
|
|
+#endif
|
|
};
|
|
|
|
static struct ipv6_devconf ipv6_devconf_dflt = {
|
|
@@ -185,54 +196,58 @@
|
|
.max_desync_factor = MAX_DESYNC_FACTOR,
|
|
#endif
|
|
.max_addresses = IPV6_MAX_ADDRESSES,
|
|
+#ifdef CONFIG_IPV6_MROUTE
|
|
+ .mc_forwarding = 0,
|
|
+#endif
|
|
};
|
|
|
|
-/* IPv6 Wildcard Address and Loopback Address defined by RFC2553 */
|
|
+/* IPv6 Wildcard Address and Loopback Address defined by RFC3493 */
|
|
#if 0
|
|
const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
|
|
#endif
|
|
const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
|
|
|
|
-int ipv6_addr_type(const struct in6_addr *addr)
|
|
+int __ipv6_addr_type(const struct in6_addr *addr)
|
|
{
|
|
- int type;
|
|
- u32 st;
|
|
+ u32 st = addr->s6_addr32[0];
|
|
|
|
- st = addr->s6_addr32[0];
|
|
+ /* Consider all addresses with the first three bits different of
|
|
+ 000 and 111 as unicasts.
|
|
+ */
|
|
+ if ((st & htonl(0xE0000000)) != htonl(0x00000000) &&
|
|
+ (st & htonl(0xE0000000)) != htonl(0xE0000000))
|
|
+ return (IPV6_ADDR_UNICAST |
|
|
+ IPV6_ADDR_SCOPE_GLOBAL<<16);
|
|
|
|
if ((st & htonl(0xFF000000)) == htonl(0xFF000000)) {
|
|
- type = IPV6_ADDR_MULTICAST;
|
|
+ /* multicast */
|
|
+ /* addr-select 3.1 */
|
|
+ int type = IPV6_ADDR_MC_SCOPE(addr)<<16;
|
|
|
|
- switch((st & htonl(0x00FF0000))) {
|
|
- case __constant_htonl(0x00010000):
|
|
+ switch(type) {
|
|
+ case IPV6_ADDR_SCOPE_NODELOCAL<<16:
|
|
type |= IPV6_ADDR_LOOPBACK;
|
|
break;
|
|
|
|
- case __constant_htonl(0x00020000):
|
|
+ case IPV6_ADDR_SCOPE_LINKLOCAL<<16:
|
|
type |= IPV6_ADDR_LINKLOCAL;
|
|
break;
|
|
|
|
- case __constant_htonl(0x00050000):
|
|
+ case IPV6_ADDR_SCOPE_SITELOCAL<<16:
|
|
type |= IPV6_ADDR_SITELOCAL;
|
|
break;
|
|
};
|
|
+ type |= IPV6_ADDR_MULTICAST;
|
|
return type;
|
|
}
|
|
|
|
- type = IPV6_ADDR_UNICAST;
|
|
-
|
|
- /* Consider all addresses with the first three bits different of
|
|
- 000 and 111 as finished.
|
|
- */
|
|
- if ((st & htonl(0xE0000000)) != htonl(0x00000000) &&
|
|
- (st & htonl(0xE0000000)) != htonl(0xE0000000))
|
|
- return type;
|
|
-
|
|
if ((st & htonl(0xFFC00000)) == htonl(0xFE800000))
|
|
- return (IPV6_ADDR_LINKLOCAL | type);
|
|
+ return (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST |
|
|
+ IPV6_ADDR_SCOPE_LINKLOCAL<<16); /* addr-select 3.1 */
|
|
|
|
if ((st & htonl(0xFFC00000)) == htonl(0xFEC00000))
|
|
- return (IPV6_ADDR_SITELOCAL | type);
|
|
+ return (IPV6_ADDR_SITELOCAL | IPV6_ADDR_UNICAST |
|
|
+ IPV6_ADDR_SCOPE_SITELOCAL<<16); /* addr-select 3.1 */
|
|
|
|
if ((addr->s6_addr32[0] | addr->s6_addr32[1]) == 0) {
|
|
if (addr->s6_addr32[2] == 0) {
|
|
@@ -240,24 +255,50 @@
|
|
return IPV6_ADDR_ANY;
|
|
|
|
if (addr->s6_addr32[3] == htonl(0x00000001))
|
|
- return (IPV6_ADDR_LOOPBACK | type);
|
|
+ return (IPV6_ADDR_LOOPBACK | IPV6_ADDR_UNICAST |
|
|
+ IPV6_ADDR_SCOPE_LINKLOCAL<<16); /* addr-select 3.4 */
|
|
|
|
- return (IPV6_ADDR_COMPATv4 | type);
|
|
+ return (IPV6_ADDR_COMPATv4 | IPV6_ADDR_UNICAST |
|
|
+ IPV6_ADDR_SCOPE_GLOBAL<<16); /* addr-select 3.3 */
|
|
}
|
|
|
|
if (addr->s6_addr32[2] == htonl(0x0000ffff))
|
|
- return IPV6_ADDR_MAPPED;
|
|
+ return (IPV6_ADDR_MAPPED |
|
|
+ IPV6_ADDR_SCOPE_GLOBAL<<16); /* addr-select 3.3 */
|
|
+ }
|
|
+
|
|
+ return (IPV6_ADDR_RESERVED |
|
|
+ IPV6_ADDR_SCOPE_GLOBAL<<16); /* addr-select 3.4 */
|
|
+}
|
|
+
|
|
+/* find 1st bit in difference between the 2 addrs */
|
|
+static inline int addr_diff(const void *__a1, const void *__a2, int addrlen)
|
|
+{
|
|
+ /* find 1st bit in difference between the 2 addrs.
|
|
+ * bit may be an invalid value,
|
|
+ * but if it is >= plen, the value is ignored in any case.
|
|
+ */
|
|
+ const u32 *a1 = __a1;
|
|
+ const u32 *a2 = __a2;
|
|
+ int i;
|
|
+
|
|
+ addrlen >>= 2;
|
|
+ for (i = 0; i < addrlen; i++) {
|
|
+ u32 xb = a1[i] ^ a2[i];
|
|
+ if (xb) {
|
|
+ int j = 31;
|
|
+ xb = ntohl(xb);
|
|
+ while ((xb & (1 << j)) == 0)
|
|
+ j--;
|
|
+ return (i * 32 + 31 - j);
|
|
+ }
|
|
}
|
|
+ return addrlen<<5;
|
|
+}
|
|
|
|
- st &= htonl(0xFF000000);
|
|
- if (st == 0)
|
|
- return IPV6_ADDR_RESERVED;
|
|
- st &= htonl(0xFE000000);
|
|
- if (st == htonl(0x02000000))
|
|
- return IPV6_ADDR_RESERVED; /* for NSAP */
|
|
- if (st == htonl(0x04000000))
|
|
- return IPV6_ADDR_RESERVED; /* for IPX */
|
|
- return type;
|
|
+static inline int ipv6_addr_diff(const struct in6_addr *a1, const struct in6_addr *a2)
|
|
+{
|
|
+ return addr_diff(a1->s6_addr, a2->s6_addr, sizeof(struct in6_addr));
|
|
}
|
|
|
|
static void addrconf_del_timer(struct inet6_ifaddr *ifp)
|
|
@@ -366,7 +407,9 @@
|
|
|
|
#ifdef CONFIG_IPV6_PRIVACY
|
|
get_random_bytes(ndev->rndid, sizeof(ndev->rndid));
|
|
+#ifdef CONFIG_IPV6_PRIVACY_MD5
|
|
get_random_bytes(ndev->entropy, sizeof(ndev->entropy));
|
|
+#endif /* CONFIG_IPV6_PRIVACY_MD5 */
|
|
init_timer(&ndev->regen_timer);
|
|
ndev->regen_timer.function = ipv6_regen_rndid;
|
|
ndev->regen_timer.data = (unsigned long) ndev;
|
|
@@ -391,14 +434,14 @@
|
|
ndev->tstamp = jiffies;
|
|
#ifdef CONFIG_SYSCTL
|
|
neigh_sysctl_register(dev, ndev->nd_parms, NET_IPV6,
|
|
- NET_IPV6_NEIGH, "ipv6", &ndisc_ifinfo_sysctl_change);
|
|
+ NET_IPV6_NEIGH, "ipv6", &ndisc_ifinfo_sysctl_change, NULL);
|
|
addrconf_sysctl_register(ndev, &ndev->cnf);
|
|
#endif
|
|
}
|
|
return ndev;
|
|
}
|
|
|
|
-static struct inet6_dev * ipv6_find_idev(struct net_device *dev)
|
|
+struct inet6_dev * ipv6_find_idev(struct net_device *dev)
|
|
{
|
|
struct inet6_dev *idev;
|
|
|
|
@@ -589,6 +632,10 @@
|
|
struct inet6_ifaddr *ifa, **ifap;
|
|
struct inet6_dev *idev = ifp->idev;
|
|
int hash;
|
|
+#ifdef CONFIG_IPV6_DELPREFIX
|
|
+ int deleted = 0, onlink = 0;
|
|
+ unsigned long expires = jiffies;
|
|
+#endif
|
|
|
|
hash = ipv6_addr_hash(&ifp->addr);
|
|
|
|
@@ -631,7 +678,32 @@
|
|
*ifap = ifa->if_next;
|
|
__in6_ifa_put(ifp);
|
|
ifa->if_next = NULL;
|
|
+#ifndef CONFIG_IPV6_DELPREFIX
|
|
break;
|
|
+#else
|
|
+ if (!(ifp->flags & IFA_F_PERMANENT) ||
|
|
+ onlink > 0)
|
|
+ break;
|
|
+ deleted = 1;
|
|
+ } else {
|
|
+ if (ipv6_prefix_equal(&ifa->addr, &ifp->addr,
|
|
+ ifp->prefix_len)) {
|
|
+ if (ifa->flags & IFA_F_PERMANENT) {
|
|
+ onlink = 1;
|
|
+ if (deleted)
|
|
+ break;
|
|
+ } else if (!onlink) {
|
|
+ unsigned long lifetime;
|
|
+ onlink = -1;
|
|
+
|
|
+ lifetime = ifp->valid_lft;
|
|
+ if (lifetime > 0x7fffffffUL/HZ)
|
|
+ lifetime = 0x7ffffffUL/HZ;
|
|
+ if (time_before(expires, ifp->tstamp + lifetime * HZ))
|
|
+ expires = ifp->tstamp + lifetime * HZ;
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
}
|
|
}
|
|
write_unlock_bh(&idev->lock);
|
|
@@ -642,6 +714,52 @@
|
|
|
|
addrconf_del_timer(ifp);
|
|
|
|
+#ifdef CONFIG_IPV6_DELPREFIX
|
|
+ /*
|
|
+ * Clean-up on-link route.
|
|
+ * We need to respect prefix lifetime.
|
|
+ *
|
|
+ * 1) if the address was not permanent, don't delete it.
|
|
+ * - timer for fib entry will delete itself.
|
|
+ * 2) if there're other permanent addresses with same prefix, don't
|
|
+ * delete it.
|
|
+ * 3) if there're only dynamic addresses, change prefix route to
|
|
+ * dynamic.
|
|
+ * - lifetime will be set to the longest valid lifetime among the
|
|
+ * addresses with same prefix on the device
|
|
+ * - subsequent RA will update lifetime.
|
|
+ * 4) otherwise, delete it.
|
|
+ *
|
|
+ * --yoshfuji
|
|
+ */
|
|
+ if ((ifp->flags & IFA_F_PERMANENT) && onlink < 1) {
|
|
+ struct in6_addr prefix;
|
|
+ struct rt6_info *rt;
|
|
+
|
|
+ ipv6_addr_prefix(&prefix, &ifp->addr, ifp->prefix_len);
|
|
+ rt = rt6_lookup(&prefix, NULL, ifp->idev->dev->ifindex, 1);
|
|
+
|
|
+ if (rt && ((rt->rt6i_flags & (RTF_GATEWAY | RTF_DEFAULT)) == 0)) {
|
|
+ if (onlink == 0) {
|
|
+ ip6_del_rt(rt, NULL, NULL);
|
|
+ rt = NULL;
|
|
+ } else if (!(rt->rt6i_flags & RTF_EXPIRES)) {
|
|
+ /*
|
|
+ * prefix lifetime and valid lifetime are
|
|
+ * (almost) the same.
|
|
+ * subsequent RA will update appropriately.
|
|
+ * --yoshfuji
|
|
+ */
|
|
+ if (expires < jiffies)
|
|
+ expires = jiffies;
|
|
+ rt->rt6i_expires = expires;
|
|
+ rt->rt6i_flags |= RTF_EXPIRES;
|
|
+ }
|
|
+ }
|
|
+ dst_release(&rt->u.dst);
|
|
+ }
|
|
+#endif
|
|
+
|
|
in6_ifa_put(ifp);
|
|
}
|
|
|
|
@@ -743,147 +861,215 @@
|
|
|
|
/*
|
|
* Choose an appropriate source address
|
|
+ * draft-ietf-ipv6-default-addr-select-09.txt
|
|
* should do:
|
|
* i) get an address with an appropriate scope
|
|
* ii) see if there is a specific route for the destination and use
|
|
* an address of the attached interface
|
|
* iii) don't use deprecated addresses
|
|
*/
|
|
-static int inline ipv6_saddr_pref(const struct inet6_ifaddr *ifp, u8 invpref)
|
|
+#define IPV6_SADDRSELECT_SELF 0x01
|
|
+#define IPV6_SADDRSELECT_PREFERRED 0x02
|
|
+#define IPV6_SADDRSELECT_HOME 0x04
|
|
+#define IPV6_SADDRSELECT_PUBLIC 0x08
|
|
+#define IPV6_SADDRSELECT_INTERFACE 0x10
|
|
+#define IPV6_SADDRSELECT_LABEL 0x20
|
|
+
|
|
+struct addrselect_attrs {
|
|
+ struct inet6_ifaddr *ifp;
|
|
+ u16 flags;
|
|
+ s16 matchlen;
|
|
+ u8 scope;
|
|
+};
|
|
+
|
|
+static int __inline__ ipv6_addrselect_preferred(int type)
|
|
{
|
|
- int pref;
|
|
- pref = ifp->flags&IFA_F_DEPRECATED ? 0 : 2;
|
|
-#ifdef CONFIG_IPV6_PRIVACY
|
|
- pref |= (ifp->flags^invpref)&IFA_F_TEMPORARY ? 0 : 1;
|
|
-#endif
|
|
- return pref;
|
|
+ /* section 3.3, 3.4 */
|
|
+ if (type&(IPV6_ADDR_MAPPED|IPV6_ADDR_COMPATv4|
|
|
+ IPV6_ADDR_LOOPBACK|IPV6_ADDR_RESERVED))
|
|
+ return 1;
|
|
+ return 0;
|
|
}
|
|
|
|
-#ifdef CONFIG_IPV6_PRIVACY
|
|
-#define IPV6_GET_SADDR_MAXSCORE(score) ((score) == 3)
|
|
-#else
|
|
-#define IPV6_GET_SADDR_MAXSCORE(score) (score)
|
|
-#endif
|
|
-
|
|
-int ipv6_dev_get_saddr(struct net_device *dev,
|
|
+int ipv6_dev_get_saddr(struct net_device *daddr_dev,
|
|
struct in6_addr *daddr, struct in6_addr *saddr)
|
|
{
|
|
- struct inet6_ifaddr *ifp = NULL;
|
|
- struct inet6_ifaddr *match = NULL;
|
|
+ int daddr_type, daddr_scope;
|
|
+ u32 daddr_label;
|
|
+ struct inet6_ifaddr *ifp0, *ifp = NULL;
|
|
+ struct net_device *dev;
|
|
struct inet6_dev *idev;
|
|
- int scope;
|
|
+
|
|
int err;
|
|
- int hiscore = -1, score;
|
|
+ int update;
|
|
+ struct addrselect_attrs candidate = {NULL,0,0,0};
|
|
+#if defined(CONFIG_IPV6_PRIVACY)
|
|
+ u16 invpref = 0;
|
|
+#endif
|
|
|
|
- scope = ipv6_addr_scope(daddr);
|
|
+#ifdef CONFIG_IPV6_PRIVACY
|
|
+ if (ipv6_devconf.use_tempaddr > 1)
|
|
+ invpref |= IPV6_SADDRSELECT_PUBLIC;
|
|
+#endif
|
|
|
|
- /*
|
|
- * known dev
|
|
- * search dev and walk through dev addresses
|
|
- */
|
|
+ daddr_type = __ipv6_addr_type(daddr);
|
|
+ daddr_scope = __ipv6_addr_src_scope(daddr_type);
|
|
+ daddr_label = ipv6_addrselect_label_lookup(daddr,
|
|
+ daddr_dev?daddr_dev->ifindex:0);
|
|
|
|
- if (dev) {
|
|
- if (dev->flags & IFF_LOOPBACK)
|
|
- scope = IFA_HOST;
|
|
+ read_lock(&dev_base_lock);
|
|
+ read_lock(&addrconf_lock);
|
|
+ for (dev = dev_base; dev; dev=dev->next) {
|
|
+ /* Rule 0: Candidate Source Address (section 4)
|
|
+ * - multicast and link-local destination address,
|
|
+ * the set of candidate source address MUST only
|
|
+ * include addresses assigned to interfaces
|
|
+ * belonging to the same link as the outgoing
|
|
+ * interface.
|
|
+ * (- For site-local destination addresses, the
|
|
+ * set of candidate source addresses MUST only
|
|
+ * include addresses assigned to interfaces
|
|
+ * belonging to the same site as the outgoing
|
|
+ * interface.)
|
|
+ */
|
|
+ if ((daddr_type&IPV6_ADDR_MULTICAST ||
|
|
+ daddr_scope <= IPV6_ADDR_SCOPE_LINKLOCAL) &&
|
|
+ daddr_dev && dev != daddr_dev)
|
|
+ continue;
|
|
|
|
- read_lock(&addrconf_lock);
|
|
idev = __in6_dev_get(dev);
|
|
- if (idev) {
|
|
- read_lock_bh(&idev->lock);
|
|
- for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
|
|
- if (ifp->scope == scope) {
|
|
- if (ifp->flags&IFA_F_TENTATIVE)
|
|
- continue;
|
|
-#ifdef CONFIG_IPV6_PRIVACY
|
|
- score = ipv6_saddr_pref(ifp, idev->cnf.use_tempaddr > 1 ? IFA_F_TEMPORARY : 0);
|
|
-#else
|
|
- score = ipv6_saddr_pref(ifp, 0);
|
|
-#endif
|
|
- if (score <= hiscore)
|
|
- continue;
|
|
+ if (!idev)
|
|
+ continue;
|
|
|
|
- if (match)
|
|
- in6_ifa_put(match);
|
|
- match = ifp;
|
|
- hiscore = score;
|
|
- in6_ifa_hold(ifp);
|
|
+ read_lock_bh(&idev->lock);
|
|
+ ifp0 = idev->addr_list;
|
|
+ for (ifp=ifp0; ifp; ifp=ifp->if_next) {
|
|
+ struct addrselect_attrs temp = {NULL,0,0,0};
|
|
+ int addr_type;
|
|
+ update = 0;
|
|
+
|
|
+ /* Rule 0: Candidate Source Address (section 4)
|
|
+ * - In any case, anycast addresses, multicast
|
|
+ * addresses, and the unspecified address MUST
|
|
+ * NOT be included in a candidate set.
|
|
+ */
|
|
+ addr_type = __ipv6_addr_type(&ifp->addr);
|
|
+ if (addr_type == IPV6_ADDR_ANY ||
|
|
+ addr_type&IPV6_ADDR_MULTICAST)
|
|
+ continue;
|
|
|
|
- if (IPV6_GET_SADDR_MAXSCORE(score)) {
|
|
- read_unlock_bh(&idev->lock);
|
|
- read_unlock(&addrconf_lock);
|
|
- goto out;
|
|
- }
|
|
+ /* Rule 1: Prefer same address */
|
|
+ if (ipv6_addr_cmp(&ifp->addr, daddr) == 0)
|
|
+ temp.flags |= IPV6_SADDRSELECT_SELF;
|
|
+ if ((temp.flags^candidate.flags)&IPV6_SADDRSELECT_SELF) {
|
|
+ update = temp.flags&IPV6_SADDRSELECT_SELF;
|
|
+ if (!update) {
|
|
+ continue;
|
|
}
|
|
}
|
|
- read_unlock_bh(&idev->lock);
|
|
- }
|
|
- read_unlock(&addrconf_lock);
|
|
- }
|
|
|
|
- if (scope == IFA_LINK)
|
|
- goto out;
|
|
+ /* Rule 2: Prefer appropriate scope */
|
|
+ temp.scope = __ipv6_addr_src_scope(addr_type);
|
|
+ if (!update) {
|
|
+ update = temp.scope - candidate.scope;
|
|
+ if (update > 0) {
|
|
+ update = candidate.scope < daddr_scope ? 1 : -1;
|
|
+ } else if (update < 0) {
|
|
+ update = temp.scope < daddr_scope ? -1 : 1;
|
|
+ }
|
|
+ if (update < 0) {
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
|
|
- /*
|
|
- * dev == NULL or search failed for specified dev
|
|
- */
|
|
+ /* Rule 3: Avoid deprecated address */
|
|
+ if (ipv6_addrselect_preferred(addr_type) ||
|
|
+ !(ifp->flags & IFA_F_DEPRECATED))
|
|
+ temp.flags |= IPV6_SADDRSELECT_PREFERRED;
|
|
+ if (!update && (temp.flags^candidate.flags)&IPV6_SADDRSELECT_PREFERRED) {
|
|
+ update = temp.flags&IPV6_SADDRSELECT_PREFERRED;
|
|
+ if (!update) {
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
|
|
- read_lock(&dev_base_lock);
|
|
- read_lock(&addrconf_lock);
|
|
- for (dev = dev_base; dev; dev=dev->next) {
|
|
- idev = __in6_dev_get(dev);
|
|
- if (idev) {
|
|
- read_lock_bh(&idev->lock);
|
|
- for (ifp=idev->addr_list; ifp; ifp=ifp->if_next) {
|
|
- if (ifp->scope == scope) {
|
|
- if (ifp->flags&IFA_F_TENTATIVE)
|
|
- continue;
|
|
+ /* XXX: Rule 4: Prefer home address */
|
|
+
|
|
+ /* Rule 5: Prefer outgoing interface */
|
|
+ if (daddr_dev == NULL || daddr_dev == dev)
|
|
+ temp.flags |= IPV6_SADDRSELECT_INTERFACE;
|
|
+ if (!update && (temp.flags^candidate.flags)&IPV6_SADDRSELECT_INTERFACE) {
|
|
+ update = temp.flags&IPV6_SADDRSELECT_INTERFACE;
|
|
+ if (!update) {
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* XXX: Rule 6: Prefer matching label */
|
|
+ if (ipv6_addrselect_label_lookup(&ifp->addr, dev->ifindex) == daddr_label)
|
|
+ temp.flags |= IPV6_SADDRSELECT_LABEL;
|
|
+ if (!update && (temp.flags^candidate.flags)&IPV6_SADDRSELECT_LABEL) {
|
|
+ update = temp.flags&IPV6_SADDRSELECT_LABEL;
|
|
+ if (!update) {
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* XXX: Rule 7: Prefer public address */
|
|
#ifdef CONFIG_IPV6_PRIVACY
|
|
- score = ipv6_saddr_pref(ifp, idev->cnf.use_tempaddr > 1 ? IFA_F_TEMPORARY : 0);
|
|
-#else
|
|
- score = ipv6_saddr_pref(ifp, 0);
|
|
+ if (!(ifp->flags & IFA_F_TEMPORARY))
|
|
+ temp.flags |= IPV6_SADDRSELECT_PUBLIC;
|
|
+ if (!update && (temp.flags^candidate.flags)&IPV6_SADDRSELECT_PUBLIC) {
|
|
+ update = (temp.flags^invpref)&IPV6_SADDRSELECT_PUBLIC;
|
|
+ if (!update) {
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
#endif
|
|
- if (score <= hiscore)
|
|
- continue;
|
|
-
|
|
- if (match)
|
|
- in6_ifa_put(match);
|
|
- match = ifp;
|
|
- hiscore = score;
|
|
- in6_ifa_hold(ifp);
|
|
|
|
- if (IPV6_GET_SADDR_MAXSCORE(score)) {
|
|
- read_unlock_bh(&idev->lock);
|
|
- goto out_unlock_base;
|
|
- }
|
|
+ /* Rule 8: Use longest matching prefix */
|
|
+ temp.matchlen = ipv6_addr_diff(&ifp->addr, daddr);
|
|
+ if (!update) {
|
|
+ update = temp.matchlen - candidate.matchlen;
|
|
+ if (update < 0) {
|
|
+ continue;
|
|
}
|
|
}
|
|
- read_unlock_bh(&idev->lock);
|
|
+
|
|
+ /* Final Rule */
|
|
+ if (!update && candidate.ifp) {
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* update candidate */
|
|
+ temp.ifp = ifp;
|
|
+ in6_ifa_hold(ifp);
|
|
+ if (candidate.ifp)
|
|
+ in6_ifa_put(candidate.ifp);
|
|
+ candidate = temp;
|
|
}
|
|
+ read_unlock_bh(&idev->lock);
|
|
}
|
|
-
|
|
-out_unlock_base:
|
|
read_unlock(&addrconf_lock);
|
|
read_unlock(&dev_base_lock);
|
|
|
|
-out:
|
|
- err = -EADDRNOTAVAIL;
|
|
- if (match) {
|
|
- ipv6_addr_copy(saddr, &match->addr);
|
|
+ if (candidate.ifp) {
|
|
+ ipv6_addr_copy(saddr, &candidate.ifp->addr);
|
|
+ in6_ifa_put(candidate.ifp);
|
|
err = 0;
|
|
- in6_ifa_put(match);
|
|
+ } else {
|
|
+ err = -EADDRNOTAVAIL;
|
|
}
|
|
-
|
|
return err;
|
|
}
|
|
|
|
-
|
|
int ipv6_get_saddr(struct dst_entry *dst,
|
|
struct in6_addr *daddr, struct in6_addr *saddr)
|
|
{
|
|
- return ipv6_dev_get_saddr(dst ? dst->dev : NULL, daddr, saddr);
|
|
+ return ipv6_dev_get_saddr(dst ? ((struct rt6_info *)dst)->rt6i_dev : NULL,
|
|
+ daddr, saddr);
|
|
}
|
|
|
|
-
|
|
int ipv6_get_lladdr(struct net_device *dev, struct in6_addr *addr)
|
|
{
|
|
struct inet6_dev *idev;
|
|
@@ -972,6 +1158,70 @@
|
|
return ifp;
|
|
}
|
|
|
|
+/* address selection: default policy label */
|
|
+/* XXX: user level configuration */
|
|
+static struct ipv6_addrselect_label {
|
|
+ struct in6_addr addr;
|
|
+ u16 plen;
|
|
+ u32 ifindex;
|
|
+ u32 label;
|
|
+} ipv6_addrselect_label_table[] = {
|
|
+ /* ::1/128, label = 0 */
|
|
+ {
|
|
+ .addr = IN6ADDR_LOOPBACK_INIT,
|
|
+ .plen = 128,
|
|
+ .label = 0,
|
|
+ },
|
|
+ /* ::/0, label = 1 */
|
|
+ {
|
|
+ .addr = IN6ADDR_ANY_INIT,
|
|
+ .plen = 0,
|
|
+ .label = 1,
|
|
+ },
|
|
+ /* 2002::/16, label = 2 */
|
|
+ {
|
|
+ .addr = {{{ 0x20, 0x02 }}},
|
|
+ .plen = 16,
|
|
+ .label = 2,
|
|
+ },
|
|
+ /* ::/96, label = 3 */
|
|
+ {
|
|
+ .plen = 96,
|
|
+ .label = 3,
|
|
+ },
|
|
+ /* ::ffff:0:0/96, label = 4 */
|
|
+ {
|
|
+ .addr = {{{ [10] = 0xff, [11] = 0xff }}},
|
|
+ .plen = 96,
|
|
+ .label = 4,
|
|
+ },
|
|
+ /* sentinel */
|
|
+ {
|
|
+ .label = 0xffffffff,
|
|
+ }
|
|
+};
|
|
+
|
|
+static u32 ipv6_addrselect_label_lookup(const struct in6_addr *addr,
|
|
+ int ifindex)
|
|
+{
|
|
+ struct ipv6_addrselect_label *p;
|
|
+ int plen, matchlen = -1;
|
|
+ u32 label = 0xffffffff;
|
|
+
|
|
+ for (p = ipv6_addrselect_label_table;
|
|
+ p->label != 0xffffffff;
|
|
+ p++) {
|
|
+ if (ifindex && p->ifindex && ifindex != p->ifindex)
|
|
+ continue;
|
|
+ plen = ipv6_addr_diff(addr, &p->addr);
|
|
+ if (plen < p->plen || plen < matchlen)
|
|
+ continue;
|
|
+ matchlen = plen;
|
|
+ label = p->label;
|
|
+ }
|
|
+ return label;
|
|
+}
|
|
+
|
|
int ipv6_rcv_saddr_equal(const struct sock *sk, const struct sock *sk2)
|
|
{
|
|
const struct in6_addr *sk_rcv_saddr6 = &inet6_sk(sk)->rcv_saddr;
|
|
@@ -1151,15 +1401,19 @@
|
|
/* (re)generation of randomized interface identifier (RFC 3041 3.2, 3.5) */
|
|
static int __ipv6_regen_rndid(struct inet6_dev *idev)
|
|
{
|
|
+#ifdef CONFIG_IPV6_PRIVACY_MD5
|
|
struct net_device *dev;
|
|
- struct scatterlist sg[2];
|
|
-
|
|
- sg[0].page = virt_to_page(idev->entropy);
|
|
- sg[0].offset = offset_in_page(idev->entropy);
|
|
- sg[0].length = 8;
|
|
- sg[1].page = virt_to_page(idev->work_eui64);
|
|
- sg[1].offset = offset_in_page(idev->work_eui64);
|
|
- sg[1].length = 8;
|
|
+ struct scatterlist sg[] = {
|
|
+ {
|
|
+ .page = virt_to_page(idev->entropy),
|
|
+ .offset = offset_in_page(idev->entropy),
|
|
+ .length = 8,
|
|
+ },{
|
|
+ .page = virt_to_page(idev->work_eui64),
|
|
+ .offset = offset_in_page(idev->work_eui64),
|
|
+ .length = 8,
|
|
+ }
|
|
+ };
|
|
|
|
dev = idev->dev;
|
|
|
|
@@ -1169,7 +1423,11 @@
|
|
idev);
|
|
get_random_bytes(idev->work_eui64, sizeof(idev->work_eui64));
|
|
}
|
|
+#endif /* CONFIG_IPV6_PRIVACY_MD5 */
|
|
regen:
|
|
+#ifndef CONFIG_IPV6_PRIVACY_MD5
|
|
+ get_random_bytes(idev->rndid, sizeof(idev->rndid));
|
|
+#else /* CONFIG_IPV6_PRIVACY_MD5 */
|
|
spin_lock(&md5_tfm_lock);
|
|
if (unlikely(md5_tfm == NULL)) {
|
|
spin_unlock(&md5_tfm_lock);
|
|
@@ -1181,9 +1439,14 @@
|
|
spin_unlock(&md5_tfm_lock);
|
|
|
|
memcpy(idev->rndid, &idev->work_digest[0], 8);
|
|
+#endif /* CONFIG_IPV6_PRIVACY_MD5 */
|
|
idev->rndid[0] &= ~0x02;
|
|
+#ifndef CONFIG_IPV6_PRIVACY_MD5
|
|
+
|
|
+#else /* CONFIG_IPV6_PRIVACY_MD5 */
|
|
memcpy(idev->entropy, &idev->work_digest[8], 8);
|
|
|
|
+#endif /* CONFIG_IPV6_PRIVACY_MD5 */
|
|
/*
|
|
* <draft-ietf-ipngwg-temp-addresses-v2-00.txt>:
|
|
* check if generated address is not inappropriate
|
|
@@ -1982,7 +2245,10 @@
|
|
if (idev) {
|
|
addrconf_sysctl_unregister(&idev->cnf);
|
|
neigh_sysctl_unregister(idev->nd_parms);
|
|
- neigh_sysctl_register(dev, idev->nd_parms, NET_IPV6, NET_IPV6_NEIGH, "ipv6", &ndisc_ifinfo_sysctl_change);
|
|
+ neigh_sysctl_register(dev, idev->nd_parms,
|
|
+ NET_IPV6, NET_IPV6_NEIGH,
|
|
+ "ipv6",
|
|
+ &ndisc_ifinfo_sysctl_change, NULL);
|
|
addrconf_sysctl_register(idev, &idev->cnf);
|
|
}
|
|
#endif
|
|
@@ -2427,6 +2693,10 @@
|
|
unsigned long regen_advance;
|
|
#endif
|
|
|
|
+#ifdef CONFIG_IPV6_PRIVACY
|
|
+ regen_advance = ifp->idev->cnf.regen_max_retry * ifp->idev->cnf.dad_transmits * ifp->idev->nd_parms->retrans_time / HZ;
|
|
+#endif
|
|
+
|
|
if (ifp->flags & IFA_F_PERMANENT)
|
|
continue;
|
|
|
|
@@ -2469,28 +2739,33 @@
|
|
}
|
|
#ifdef CONFIG_IPV6_PRIVACY
|
|
} else if ((ifp->flags&IFA_F_TEMPORARY) &&
|
|
- !(ifp->flags&IFA_F_TENTATIVE)) {
|
|
- if (age >= ifp->prefered_lft - regen_advance) {
|
|
- struct inet6_ifaddr *ifpub = ifp->ifpub;
|
|
- if (time_before(ifp->tstamp + ifp->prefered_lft * HZ, next))
|
|
- next = ifp->tstamp + ifp->prefered_lft * HZ;
|
|
- if (!ifp->regen_count && ifpub) {
|
|
- ifp->regen_count++;
|
|
- in6_ifa_hold(ifp);
|
|
- in6_ifa_hold(ifpub);
|
|
- spin_unlock(&ifp->lock);
|
|
- write_unlock(&addrconf_hash_lock);
|
|
- ipv6_create_tempaddr(ifpub, ifp);
|
|
- in6_ifa_put(ifpub);
|
|
- in6_ifa_put(ifp);
|
|
- goto restart;
|
|
- }
|
|
- } else if (time_before(ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ, next))
|
|
- next = ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ;
|
|
- spin_unlock(&ifp->lock);
|
|
+ !(ifp->flags&IFA_F_TENTATIVE) &&
|
|
+ age >= ifp->prefered_lft - regen_advance) {
|
|
+ struct inet6_ifaddr *ifpub = ifp->ifpub;
|
|
+ if (time_before(ifp->tstamp + ifp->prefered_lft * HZ, next))
|
|
+ next = ifp->tstamp + ifp->prefered_lft * HZ;
|
|
+ if (!ifp->regen_count && ifpub) {
|
|
+ ifp->regen_count++;
|
|
+ in6_ifa_hold(ifp);
|
|
+ in6_ifa_hold(ifpub);
|
|
+ spin_unlock(&ifp->lock);
|
|
+ write_unlock(&addrconf_hash_lock);
|
|
+ ipv6_create_tempaddr(ifpub, ifp);
|
|
+ in6_ifa_put(ifpub);
|
|
+ in6_ifa_put(ifp);
|
|
+ goto restart;
|
|
+ } else {
|
|
+ spin_unlock(&ifp->lock);
|
|
+ }
|
|
#endif
|
|
} else {
|
|
/* ifp->prefered_lft <= ifp->valid_lft */
|
|
+#ifdef CONFIG_IPV6_PRIVACY
|
|
+ if (ifp->flags&IFA_F_TEMPORARY) {
|
|
+ if (time_before(ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ, next))
|
|
+ next = ifp->tstamp + ifp->prefered_lft * HZ - regen_advance * HZ;
|
|
+ } else
|
|
+#endif
|
|
if (time_before(ifp->tstamp + ifp->prefered_lft * HZ, next))
|
|
next = ifp->tstamp + ifp->prefered_lft * HZ;
|
|
spin_unlock(&ifp->lock);
|
|
@@ -2835,6 +3110,9 @@
|
|
array[DEVCONF_MAX_DESYNC_FACTOR] = cnf->max_desync_factor;
|
|
#endif
|
|
array[DEVCONF_MAX_ADDRESSES] = cnf->max_addresses;
|
|
+#ifdef CONFIG_IPV6_MROUTE
|
|
+ array[DEVCONF_MCFORWARDING] = cnf->mc_forwarding;
|
|
+#endif
|
|
}
|
|
|
|
static int inet6_fill_ifinfo(struct sk_buff *skb, struct inet6_dev *idev,
|
|
@@ -3147,7 +3425,7 @@
|
|
static struct addrconf_sysctl_table
|
|
{
|
|
struct ctl_table_header *sysctl_header;
|
|
- ctl_table addrconf_vars[18];
|
|
+ ctl_table addrconf_vars[19];
|
|
ctl_table addrconf_dev[2];
|
|
ctl_table addrconf_conf_dir[2];
|
|
ctl_table addrconf_proto_dir[2];
|
|
@@ -3296,6 +3574,16 @@
|
|
.mode = 0644,
|
|
.proc_handler = &proc_dointvec,
|
|
},
|
|
+#ifdef CONFIG_IPV6_MROUTE
|
|
+ {
|
|
+ .ctl_name = NET_IPV6_MC_FORWARDING,
|
|
+ .procname = "mc_forwarding",
|
|
+ .data = &ipv6_devconf.mc_forwarding,
|
|
+ .maxlen = sizeof(int),
|
|
+ .mode = 0644,
|
|
+ .proc_handler = &proc_dointvec,
|
|
+ },
|
|
+#endif
|
|
{
|
|
.ctl_name = 0, /* sentinel */
|
|
}
|
|
@@ -3440,6 +3728,9 @@
|
|
|
|
void __init addrconf_init(void)
|
|
{
|
|
+#ifdef CONFIG_IPV6_PRIVACY_MD5
|
|
+ struct crypto_tfm *tfm;
|
|
+#endif /* CONFIG_IPV6_PRIVACY_MD5 */
|
|
/* The addrconf netdev notifier requires that loopback_dev
|
|
* has it's ipv6 private information allocated and setup
|
|
* before it can bring up and give link-local addresses
|
|
@@ -3465,12 +3756,22 @@
|
|
|
|
register_netdevice_notifier(&ipv6_dev_notf);
|
|
|
|
-#ifdef CONFIG_IPV6_PRIVACY
|
|
- md5_tfm = crypto_alloc_tfm("md5", 0);
|
|
- if (unlikely(md5_tfm == NULL))
|
|
+#ifdef CONFIG_IPV6_PRIVACY_MD5
|
|
+ tfm = crypto_alloc_tfm("md5", 0);
|
|
+ if (likely(tfm != NULL)) {
|
|
+ spin_lock(&md5_tfm_lock);
|
|
+ if (likely(md5_tfm == NULL)) {
|
|
+ md5_tfm = tfm;
|
|
+ spin_unlock(&md5_tfm_lock);
|
|
+ } else {
|
|
+ spin_unlock(&md5_tfm_lock);
|
|
+ crypto_free_tfm(tfm);
|
|
+ }
|
|
+ } else {
|
|
printk(KERN_WARNING
|
|
"failed to load transform for md5\n");
|
|
-#endif
|
|
+ }
|
|
+#endif /* CONFIG_IPV6_PRIVACY_MD5 */
|
|
|
|
addrconf_verify(0);
|
|
rtnetlink_links[PF_INET6] = inet6_rtnetlink_table;
|
|
@@ -3487,6 +3788,9 @@
|
|
struct inet6_dev *idev;
|
|
struct inet6_ifaddr *ifa;
|
|
int i;
|
|
+#ifdef CONFIG_IPV6_PRIVACY_MD5
|
|
+ struct crypto_tfm *tfm;
|
|
+#endif /* CONFIG_IPV6_PRIVACY_MD5 */
|
|
|
|
unregister_netdevice_notifier(&ipv6_dev_notf);
|
|
|
|
@@ -3531,12 +3835,14 @@
|
|
|
|
rtnl_unlock();
|
|
|
|
-#ifdef CONFIG_IPV6_PRIVACY
|
|
- if (likely(md5_tfm != NULL)) {
|
|
- crypto_free_tfm(md5_tfm);
|
|
- md5_tfm = NULL;
|
|
- }
|
|
-#endif
|
|
+#ifdef CONFIG_IPV6_PRIVACY_MD5
|
|
+ spin_lock(&md5_tfm_lock);
|
|
+ tfm = md5_tfm;
|
|
+ md5_tfm = NULL;
|
|
+ spin_unlock(&md5_tfm_lock);
|
|
+ if (likely(tfm))
|
|
+ crypto_free_tfm(tfm);
|
|
+#endif /* CONFIG_IPV6_PRIVACY_MD5 */
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
proc_net_remove("if_inet6");
|
|
diff -urN linux-2.6.11/net/ipv6/af_inet6.c x1/net/ipv6/af_inet6.c
|
|
--- linux-2.6.11/net/ipv6/af_inet6.c 2005-03-02 08:38:10.000000000 +0100
|
|
+++ x1/net/ipv6/af_inet6.c 2005-03-02 17:30:59.000000000 +0100
|
|
@@ -13,6 +13,7 @@
|
|
* piggy, Karl Knutson : Socket protocol table
|
|
* Hideaki YOSHIFUJI : sin6_scope_id support
|
|
* Arnaldo Melo : check proc_net_create return, cleanups
|
|
+ * Hoerdt Mickael : Added Multicast routing support.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
@@ -62,6 +63,9 @@
|
|
|
|
#include <asm/uaccess.h>
|
|
#include <asm/system.h>
|
|
+#ifdef CONFIG_IPV6_MROUTE
|
|
+#include <linux/mroute6.h>
|
|
+#endif
|
|
|
|
MODULE_AUTHOR("Cast of dozens");
|
|
MODULE_DESCRIPTION("IPv6 protocol stack for Linux");
|
|
@@ -758,6 +762,9 @@
|
|
err = icmpv6_init(&inet6_family_ops);
|
|
if (err)
|
|
goto icmp_fail;
|
|
+#ifdef CONFIG_IPV6_MROUTE
|
|
+ ip6_mr_init();
|
|
+#endif
|
|
err = ndisc_init(&inet6_family_ops);
|
|
if (err)
|
|
goto ndisc_fail;
|
|
diff -urN linux-2.6.11/net/ipv6/ah6.c x1/net/ipv6/ah6.c
|
|
--- linux-2.6.11/net/ipv6/ah6.c 2005-03-02 08:38:38.000000000 +0100
|
|
+++ x1/net/ipv6/ah6.c 2005-02-03 06:35:55.000000000 +0100
|
|
@@ -264,6 +264,8 @@
|
|
|
|
hdr_len = skb->data - skb->nh.raw;
|
|
ah = (struct ipv6_auth_hdr*)skb->data;
|
|
+ if (x->props.replay_window && xfrm_replay_check(x, ah->seq_no))
|
|
+ goto out;
|
|
ahp = x->data;
|
|
nexthdr = ah->nexthdr;
|
|
ah_hlen = (ah->hdrlen + 2) << 2;
|
|
@@ -302,6 +304,8 @@
|
|
}
|
|
}
|
|
|
|
+ if (x->props.replay_window)
|
|
+ xfrm_replay_advance(x, ah->seq_no);
|
|
skb->nh.raw = skb_pull(skb, ah_hlen);
|
|
memcpy(skb->nh.raw, tmp_hdr, hdr_len);
|
|
skb->nh.ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
|
|
diff -urN linux-2.6.11/net/ipv6/anycast.c x1/net/ipv6/anycast.c
|
|
--- linux-2.6.11/net/ipv6/anycast.c 2005-03-02 08:38:34.000000000 +0100
|
|
+++ x1/net/ipv6/anycast.c 2005-02-03 15:43:50.000000000 +0100
|
|
@@ -48,32 +48,6 @@
|
|
/* Big ac list lock for all the sockets */
|
|
static DEFINE_RWLOCK(ipv6_sk_ac_lock);
|
|
|
|
-/* XXX ip6_addr_match() and ip6_onlink() really belong in net/core.c */
|
|
-
|
|
-static int
|
|
-ip6_addr_match(struct in6_addr *addr1, struct in6_addr *addr2, int prefix)
|
|
-{
|
|
- __u32 mask;
|
|
- int i;
|
|
-
|
|
- if (prefix > 128 || prefix < 0)
|
|
- return 0;
|
|
- if (prefix == 0)
|
|
- return 1;
|
|
- for (i=0; i<4; ++i) {
|
|
- if (prefix >= 32)
|
|
- mask = ~0;
|
|
- else
|
|
- mask = htonl(~0 << (32 - prefix));
|
|
- if ((addr1->s6_addr32[i] ^ addr2->s6_addr32[i]) & mask)
|
|
- return 0;
|
|
- prefix -= 32;
|
|
- if (prefix <= 0)
|
|
- break;
|
|
- }
|
|
- return 1;
|
|
-}
|
|
-
|
|
static int
|
|
ip6_onlink(struct in6_addr *addr, struct net_device *dev)
|
|
{
|
|
@@ -87,8 +61,8 @@
|
|
if (idev) {
|
|
read_lock_bh(&idev->lock);
|
|
for (ifa=idev->addr_list; ifa; ifa=ifa->if_next) {
|
|
- onlink = ip6_addr_match(addr, &ifa->addr,
|
|
- ifa->prefix_len);
|
|
+ onlink = ipv6_prefix_equal(addr, &ifa->addr,
|
|
+ ifa->prefix_len);
|
|
if (onlink)
|
|
break;
|
|
}
|
|
diff -urN linux-2.6.11/net/ipv6/esp6.c x1/net/ipv6/esp6.c
|
|
--- linux-2.6.11/net/ipv6/esp6.c 2005-03-02 08:38:12.000000000 +0100
|
|
+++ x1/net/ipv6/esp6.c 2005-02-03 06:35:55.000000000 +0100
|
|
@@ -148,6 +148,7 @@
|
|
ret = -EINVAL;
|
|
goto out_nofree;
|
|
}
|
|
+ esph = (struct ipv6_esp_hdr*)skb->data;
|
|
|
|
if (elen <= 0 || (elen & (blksize-1))) {
|
|
ret = -EINVAL;
|
|
@@ -166,6 +167,11 @@
|
|
u8 sum[esp->auth.icv_full_len];
|
|
u8 sum1[alen];
|
|
|
|
+ if (x->props.replay_window && xfrm_replay_check(x, esph->seq_no)) {
|
|
+ ret = -EINVAL;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
esp->auth.icv(esp, skb, 0, skb->len-alen, sum);
|
|
|
|
if (skb_copy_bits(skb, skb->len-alen, sum1, alen))
|
|
@@ -176,6 +182,10 @@
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
+
|
|
+ if (x->props.replay_window)
|
|
+ xfrm_replay_advance(x, esph->seq_no);
|
|
+
|
|
}
|
|
|
|
if ((nfrags = skb_cow_data(skb, 0, &trailer)) < 0) {
|
|
@@ -185,7 +195,6 @@
|
|
|
|
skb->ip_summed = CHECKSUM_NONE;
|
|
|
|
- esph = (struct ipv6_esp_hdr*)skb->data;
|
|
iph = skb->nh.ipv6h;
|
|
|
|
/* Get ivec. This can be wrong, check against another impls. */
|
|
@@ -307,7 +316,7 @@
|
|
if (x->aalg->alg_key_len > 512)
|
|
goto error;
|
|
}
|
|
- if (x->ealg == NULL)
|
|
+ if (x->ealg == NULL || (x->ealg->alg_key_len == 0 && x->props.ealgo != SADB_EALG_NULL))
|
|
goto error;
|
|
|
|
if (x->encap)
|
|
@@ -349,11 +358,13 @@
|
|
goto error;
|
|
}
|
|
esp->conf.key = x->ealg->alg_key;
|
|
- esp->conf.key_len = (x->ealg->alg_key_len+7)/8;
|
|
- if (x->props.ealgo == SADB_EALG_NULL)
|
|
+ if (x->props.ealgo == SADB_EALG_NULL) {
|
|
+ esp->conf.key_len = 0;
|
|
esp->conf.tfm = crypto_alloc_tfm(x->ealg->alg_name, CRYPTO_TFM_MODE_ECB);
|
|
- else
|
|
+ } else {
|
|
+ esp->conf.key_len = (x->ealg->alg_key_len+7)/8;
|
|
esp->conf.tfm = crypto_alloc_tfm(x->ealg->alg_name, CRYPTO_TFM_MODE_CBC);
|
|
+ }
|
|
if (esp->conf.tfm == NULL)
|
|
goto error;
|
|
esp->conf.ivlen = crypto_tfm_alg_ivsize(esp->conf.tfm);
|
|
diff -urN linux-2.6.11/net/ipv6/exthdrs.c x1/net/ipv6/exthdrs.c
|
|
--- linux-2.6.11/net/ipv6/exthdrs.c 2005-03-02 08:37:47.000000000 +0100
|
|
+++ x1/net/ipv6/exthdrs.c 2005-02-03 05:44:14.000000000 +0100
|
|
@@ -156,10 +156,18 @@
|
|
{
|
|
struct sk_buff *skb = *skbp;
|
|
struct inet6_skb_parm *opt = IP6CB(skb);
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct dst_entry *dst = skb->dst;
|
|
+ struct inet6_dev *idev = ((struct rt6_info *)dst)->rt6i_idev;
|
|
+#endif
|
|
|
|
if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
|
|
!pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
|
|
+#endif
|
|
kfree_skb(skb);
|
|
return -1;
|
|
}
|
|
@@ -172,7 +180,11 @@
|
|
return 1;
|
|
}
|
|
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
|
|
+#endif
|
|
return -1;
|
|
}
|
|
|
|
@@ -220,6 +232,10 @@
|
|
struct inet6_skb_parm *opt = IP6CB(skb);
|
|
struct in6_addr *addr;
|
|
struct in6_addr daddr;
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct dst_entry *dst = skb->dst;
|
|
+ struct inet6_dev *idev = ((struct rt6_info *)dst)->rt6i_idev;
|
|
+#endif
|
|
int n, i;
|
|
|
|
struct ipv6_rt_hdr *hdr;
|
|
@@ -227,7 +243,11 @@
|
|
|
|
if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) ||
|
|
!pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
|
|
+#endif
|
|
kfree_skb(skb);
|
|
return -1;
|
|
}
|
|
@@ -236,7 +256,11 @@
|
|
|
|
if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr) ||
|
|
skb->pkt_type != PACKET_HOST) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INADDRERRORS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
|
|
+#endif
|
|
kfree_skb(skb);
|
|
return -1;
|
|
}
|
|
@@ -252,13 +276,21 @@
|
|
}
|
|
|
|
if (hdr->type != IPV6_SRCRT_TYPE_0) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
|
|
+#endif
|
|
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw);
|
|
return -1;
|
|
}
|
|
|
|
if (hdr->hdrlen & 0x01) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
|
|
+#endif
|
|
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->hdrlen) - skb->nh.raw);
|
|
return -1;
|
|
}
|
|
@@ -271,7 +303,11 @@
|
|
n = hdr->hdrlen >> 1;
|
|
|
|
if (hdr->segments_left > n) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
|
|
+#endif
|
|
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->segments_left) - skb->nh.raw);
|
|
return -1;
|
|
}
|
|
@@ -284,7 +320,11 @@
|
|
kfree_skb(skb);
|
|
/* the copy is a forwarded packet */
|
|
if (skb2 == NULL) {
|
|
- IP6_INC_STATS_BH(IPSTATS_MIB_OUTDISCARDS);
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_OUTDISCARDS);
|
|
+#else
|
|
+ IP6_INC_STATS_BH(IPSTATS_MIB_OUTDISCARDS);
|
|
+#endif
|
|
return -1;
|
|
}
|
|
*skbp = skb = skb2;
|
|
@@ -302,7 +342,11 @@
|
|
addr += i - 1;
|
|
|
|
if (ipv6_addr_is_multicast(addr)) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INADDRERRORS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
|
|
+#endif
|
|
kfree_skb(skb);
|
|
return -1;
|
|
}
|
|
@@ -321,7 +365,11 @@
|
|
|
|
if (skb->dst->dev->flags&IFF_LOOPBACK) {
|
|
if (skb->nh.ipv6h->hop_limit <= 1) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
|
|
+#endif
|
|
icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
|
|
0, skb->dev);
|
|
kfree_skb(skb);
|
|
@@ -434,29 +482,49 @@
|
|
|
|
static int ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
|
|
{
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct dst_entry *dst = skb->dst;
|
|
+ struct inet6_dev *idev = ((struct rt6_info *)dst)->rt6i_idev;
|
|
+#endif
|
|
u32 pkt_len;
|
|
|
|
if (skb->nh.raw[optoff+1] != 4 || (optoff&3) != 2) {
|
|
LIMIT_NETDEBUG(
|
|
printk(KERN_DEBUG "ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n", skb->nh.raw[optoff+1]));
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
|
|
+#endif
|
|
goto drop;
|
|
}
|
|
|
|
pkt_len = ntohl(*(u32*)(skb->nh.raw+optoff+2));
|
|
if (pkt_len <= IPV6_MAXPLEN) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
|
|
+#endif
|
|
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2);
|
|
return 0;
|
|
}
|
|
if (skb->nh.ipv6h->payload_len) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
|
|
+#endif
|
|
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff);
|
|
return 0;
|
|
}
|
|
|
|
if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INTRUNCATEDPKTS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INTRUNCATEDPKTS);
|
|
+#endif
|
|
goto drop;
|
|
}
|
|
if (pkt_len + sizeof(struct ipv6hdr) < skb->len) {
|
|
@@ -483,11 +551,13 @@
|
|
{ -1, }
|
|
};
|
|
|
|
-int ipv6_parse_hopopts(struct sk_buff *skb, int nhoff)
|
|
+int ipv6_parse_hopopts(struct sk_buff **skbp, unsigned int *nhoffp)
|
|
{
|
|
- IP6CB(skb)->hop = sizeof(struct ipv6hdr);
|
|
- if (ip6_parse_tlv(tlvprochopopt_lst, skb))
|
|
- return sizeof(struct ipv6hdr);
|
|
+ IP6CB(*skbp)->hop = sizeof(struct ipv6hdr);
|
|
+ if (ip6_parse_tlv(tlvprochopopt_lst, *skbp)) {
|
|
+ *nhoffp = sizeof(struct ipv6hdr);
|
|
+ return 1;
|
|
+ }
|
|
return -1;
|
|
}
|
|
|
|
@@ -543,14 +613,34 @@
|
|
ipv6_push_rthdr(skb, proto, opt->srcrt, daddr);
|
|
if (opt->dst0opt)
|
|
ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst0opt);
|
|
- if (opt->hopopt)
|
|
+ if (skb->len > IPV6_MAXPLEN - (opt->hopopt ? (opt->hopopt->hdrlen+2)<<2 : 0)) {
|
|
+ /* data is jumbogram */
|
|
+ u8 *hopt = skb_push(skb, 8);
|
|
+ if (opt->hopopt) {
|
|
+ u8 *hlen;
|
|
+ ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
|
|
+ hlen = skb->data + 1;
|
|
+ (*hlen)++;
|
|
+ hopt[0] = IPV6_TLV_PADN;
|
|
+ hopt[1] = 0;
|
|
+ } else {
|
|
+ hopt[0] = *proto;
|
|
+ hopt[1] = 0;
|
|
+ }
|
|
+ hopt[2] = 0xC2;
|
|
+ hopt[3] = 4;
|
|
+ *(u32 *)&hopt[4] = htonl(skb->len);
|
|
+ *proto = NEXTHDR_HOP;
|
|
+ } else if (opt->hopopt)
|
|
ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt);
|
|
}
|
|
|
|
void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto)
|
|
{
|
|
- if (opt->dst1opt)
|
|
+ if (opt->dst1opt) {
|
|
ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt);
|
|
+ skb->h.raw = skb->data;
|
|
+ }
|
|
}
|
|
|
|
struct ipv6_txoptions *
|
|
diff -urN linux-2.6.11/net/ipv6/icmp.c x1/net/ipv6/icmp.c
|
|
--- linux-2.6.11/net/ipv6/icmp.c 2005-03-02 08:37:52.000000000 +0100
|
|
+++ x1/net/ipv6/icmp.c 2005-02-03 05:44:15.000000000 +0100
|
|
@@ -176,7 +176,11 @@
|
|
*/
|
|
dst = ip6_route_output(sk, fl);
|
|
if (dst->error) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS((((struct rt6_info *)dst)->rt6i_idev), IPSTATS_MIB_OUTNOROUTES);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTNOROUTES);
|
|
+#endif
|
|
} else if (dst->dev && (dst->dev->flags&IFF_LOOPBACK)) {
|
|
res = 1;
|
|
} else {
|
|
@@ -381,6 +385,8 @@
|
|
hlimit = np->hop_limit;
|
|
if (hlimit < 0)
|
|
hlimit = dst_metric(dst, RTAX_HOPLIMIT);
|
|
+ if (hlimit < 0)
|
|
+ hlimit = ipv6_get_hoplimit(dst->dev);
|
|
|
|
msg.skb = skb;
|
|
msg.offset = skb->nh.raw - skb->data;
|
|
@@ -467,6 +473,8 @@
|
|
hlimit = np->hop_limit;
|
|
if (hlimit < 0)
|
|
hlimit = dst_metric(dst, RTAX_HOPLIMIT);
|
|
+ if (hlimit < 0)
|
|
+ hlimit = ipv6_get_hoplimit(dst->dev);
|
|
|
|
idev = in6_dev_get(skb->dev);
|
|
|
|
diff -urN linux-2.6.11/net/ipv6/ip6_fib.c x1/net/ipv6/ip6_fib.c
|
|
--- linux-2.6.11/net/ipv6/ip6_fib.c 2005-03-02 08:38:19.000000000 +0100
|
|
+++ x1/net/ipv6/ip6_fib.c 2005-02-03 13:45:59.000000000 +0100
|
|
@@ -18,6 +18,7 @@
|
|
* Yuji SEKIYA @USAGI: Support default route on router node;
|
|
* remove ip6_null_entry from the top of
|
|
* routing table.
|
|
+ * Ville Nuorvala: Fixes to source address sub trees
|
|
*/
|
|
#include <linux/config.h>
|
|
#include <linux/errno.h>
|
|
@@ -49,6 +50,11 @@
|
|
|
|
struct rt6_statistics rt6_stats;
|
|
|
|
+#if !defined(CONFIG_IPV6_NEW_ROUNDROBIN)
|
|
+extern struct rt6_info *rt6_dflt_pointer;
|
|
+extern spinlock_t rt6_dflt_lock;
|
|
+#endif
|
|
+
|
|
static kmem_cache_t * fib6_node_kmem;
|
|
|
|
enum fib_walk_state_t
|
|
@@ -80,6 +86,7 @@
|
|
#define SUBTREE(fn) NULL
|
|
#endif
|
|
|
|
+static struct rt6_info * fib6_find_prefix(struct fib6_node *fn);
|
|
static void fib6_prune_clones(struct fib6_node *fn, struct rt6_info *rt);
|
|
static struct fib6_node * fib6_repair_tree(struct fib6_node *fn);
|
|
|
|
@@ -117,36 +124,6 @@
|
|
*/
|
|
|
|
/*
|
|
- * compare "prefix length" bits of an address
|
|
- */
|
|
-
|
|
-static __inline__ int addr_match(void *token1, void *token2, int prefixlen)
|
|
-{
|
|
- __u32 *a1 = token1;
|
|
- __u32 *a2 = token2;
|
|
- int pdw;
|
|
- int pbi;
|
|
-
|
|
- pdw = prefixlen >> 5; /* num of whole __u32 in prefix */
|
|
- pbi = prefixlen & 0x1f; /* num of bits in incomplete u32 in prefix */
|
|
-
|
|
- if (pdw)
|
|
- if (memcmp(a1, a2, pdw << 2))
|
|
- return 0;
|
|
-
|
|
- if (pbi) {
|
|
- __u32 mask;
|
|
-
|
|
- mask = htonl((0xffffffff) << (32 - pbi));
|
|
-
|
|
- if ((a1[pdw] ^ a2[pdw]) & mask)
|
|
- return 0;
|
|
- }
|
|
-
|
|
- return 1;
|
|
-}
|
|
-
|
|
-/*
|
|
* test bit
|
|
*/
|
|
|
|
@@ -261,7 +238,7 @@
|
|
* Prefix match
|
|
*/
|
|
if (plen < fn->fn_bit ||
|
|
- !addr_match(&key->addr, addr, fn->fn_bit))
|
|
+ !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit))
|
|
goto insert_above;
|
|
|
|
/*
|
|
@@ -513,6 +490,9 @@
|
|
{
|
|
struct fib6_node *fn;
|
|
int err = -ENOMEM;
|
|
+#ifdef CONFIG_IPV6_SUBTREES
|
|
+ struct fib6_node *pn = NULL;
|
|
+#endif
|
|
|
|
fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr),
|
|
rt->rt6i_dst.plen, offsetof(struct rt6_info, rt6i_dst));
|
|
@@ -565,10 +545,6 @@
|
|
/* Now link new subtree to main tree */
|
|
sfn->parent = fn;
|
|
fn->subtree = sfn;
|
|
- if (fn->leaf == NULL) {
|
|
- fn->leaf = rt;
|
|
- atomic_inc(&rt->rt6i_ref);
|
|
- }
|
|
} else {
|
|
sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr,
|
|
sizeof(struct in6_addr), rt->rt6i_src.plen,
|
|
@@ -578,6 +554,13 @@
|
|
goto st_failure;
|
|
}
|
|
|
|
+ /* fib6_add_1 might have cleared the old leaf pointer */
|
|
+ if (fn->leaf == NULL) {
|
|
+ fn->leaf = rt;
|
|
+ atomic_inc(&rt->rt6i_ref);
|
|
+ }
|
|
+
|
|
+ pn = fn;
|
|
fn = sn;
|
|
}
|
|
#endif
|
|
@@ -591,8 +574,29 @@
|
|
}
|
|
|
|
out:
|
|
- if (err)
|
|
+ if (err) {
|
|
+#ifdef CONFIG_IPV6_SUBTREES
|
|
+ /* If fib6_add_1 has cleared the old leaf pointer in the
|
|
+ * super-tree leaf node, we have to find a new one for it.
|
|
+ *
|
|
+ * This situation will never arise in the sub-tree since
|
|
+ * the node will at least have the duplicate route that
|
|
+ * caused fib6_add_rt2node to fail in the first place.
|
|
+ */
|
|
+
|
|
+ if (pn && !(pn->fn_flags & RTN_RTINFO)) {
|
|
+ pn->leaf = fib6_find_prefix(pn);
|
|
+#if RT6_DEBUG >= 2
|
|
+ if (!pn->leaf) {
|
|
+ BUG_TRAP(pn->leaf);
|
|
+ pn->leaf = &ip6_null_entry;
|
|
+ }
|
|
+#endif
|
|
+ atomic_inc(&pn->leaf->rt6i_ref);
|
|
+ }
|
|
+#endif
|
|
dst_free(&rt->u.dst);
|
|
+ }
|
|
return err;
|
|
|
|
#ifdef CONFIG_IPV6_SUBTREES
|
|
@@ -667,7 +671,7 @@
|
|
key = (struct rt6key *) ((u8 *) fn->leaf +
|
|
args->offset);
|
|
|
|
- if (addr_match(&key->addr, args->addr, key->plen))
|
|
+ if (ipv6_prefix_equal(&key->addr, args->addr, key->plen))
|
|
return fn;
|
|
}
|
|
|
|
@@ -680,16 +684,19 @@
|
|
struct fib6_node * fib6_lookup(struct fib6_node *root, struct in6_addr *daddr,
|
|
struct in6_addr *saddr)
|
|
{
|
|
- struct lookup_args args[2];
|
|
struct fib6_node *fn;
|
|
-
|
|
- args[0].offset = offsetof(struct rt6_info, rt6i_dst);
|
|
- args[0].addr = daddr;
|
|
-
|
|
+ struct lookup_args args[2] = {
|
|
+ {
|
|
+ .offset = offsetof(struct rt6_info, rt6i_dst),
|
|
+ .addr = daddr,
|
|
+ },
|
|
#ifdef CONFIG_IPV6_SUBTREES
|
|
- args[1].offset = offsetof(struct rt6_info, rt6i_src);
|
|
- args[1].addr = saddr;
|
|
+ {
|
|
+ .offset = offsetof(struct rt6_info, rt6i_src),
|
|
+ .addr = saddr,
|
|
+ },
|
|
#endif
|
|
+ };
|
|
|
|
fn = fib6_lookup_1(root, args);
|
|
|
|
@@ -718,7 +725,7 @@
|
|
* Prefix match
|
|
*/
|
|
if (plen < fn->fn_bit ||
|
|
- !addr_match(&key->addr, addr, fn->fn_bit))
|
|
+ !ipv6_prefix_equal(&key->addr, addr, fn->fn_bit))
|
|
return NULL;
|
|
|
|
if (plen == fn->fn_bit)
|
|
@@ -747,10 +754,8 @@
|
|
#ifdef CONFIG_IPV6_SUBTREES
|
|
if (src_len) {
|
|
BUG_TRAP(saddr!=NULL);
|
|
- if (fn == NULL)
|
|
- fn = fn->subtree;
|
|
if (fn)
|
|
- fn = fib6_locate_1(fn, saddr, src_len,
|
|
+ fn = fib6_locate_1(fn->subtree, saddr, src_len,
|
|
offsetof(struct rt6_info, rt6i_src));
|
|
}
|
|
#endif
|
|
@@ -1184,7 +1189,9 @@
|
|
if (rt->rt6i_flags&RTF_EXPIRES && rt->rt6i_expires) {
|
|
if (time_after(now, rt->rt6i_expires)) {
|
|
RT6_TRACE("expiring %p\n", rt);
|
|
+#if !defined(CONFIG_IPV6_NEW_ROUNDROBIN)
|
|
rt6_reset_dflt_pointer(rt);
|
|
+#endif
|
|
return -1;
|
|
}
|
|
gc_args.more++;
|
|
diff -urN linux-2.6.11/net/ipv6/ip6_input.c x1/net/ipv6/ip6_input.c
|
|
--- linux-2.6.11/net/ipv6/ip6_input.c 2005-03-02 08:38:17.000000000 +0100
|
|
+++ x1/net/ipv6/ip6_input.c 2005-02-13 19:37:56.000000000 +0100
|
|
@@ -19,6 +19,7 @@
|
|
*
|
|
* Mitsuru KANDA @USAGI and
|
|
* YOSHIFUJI Hideaki @USAGI: Remove ipv6_parse_exthdrs().
|
|
+ * Hoerdt Mickael : Added Multicast routing support.
|
|
*/
|
|
|
|
#include <linux/errno.h>
|
|
@@ -59,15 +60,27 @@
|
|
int ipv6_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
|
|
{
|
|
struct ipv6hdr *hdr;
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct inet6_dev *idev = NULL;
|
|
+#endif
|
|
u32 pkt_len;
|
|
|
|
if (skb->pkt_type == PACKET_OTHERHOST)
|
|
goto drop;
|
|
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ idev = in6_dev_get(dev);
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INRECEIVES);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INRECEIVES);
|
|
+#endif
|
|
|
|
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INDISCARDS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
|
|
+#endif
|
|
goto out;
|
|
}
|
|
|
|
@@ -79,10 +92,8 @@
|
|
if (skb->len < sizeof(struct ipv6hdr))
|
|
goto err;
|
|
|
|
- if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) {
|
|
- IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
|
|
- goto drop;
|
|
- }
|
|
+ if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
|
|
+ goto err;
|
|
|
|
hdr = skb->nh.ipv6h;
|
|
|
|
@@ -96,10 +107,8 @@
|
|
if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
|
|
goto truncated;
|
|
if (pkt_len + sizeof(struct ipv6hdr) < skb->len) {
|
|
- if (__pskb_trim(skb, pkt_len + sizeof(struct ipv6hdr))){
|
|
- IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
|
|
- goto drop;
|
|
- }
|
|
+ if (__pskb_trim(skb, pkt_len + sizeof(struct ipv6hdr)))
|
|
+ goto err;
|
|
hdr = skb->nh.ipv6h;
|
|
if (skb->ip_summed == CHECKSUM_HW)
|
|
skb->ip_summed = CHECKSUM_NONE;
|
|
@@ -107,22 +116,39 @@
|
|
}
|
|
|
|
if (hdr->nexthdr == NEXTHDR_HOP) {
|
|
+ unsigned int nhoff = offsetof(struct ipv6hdr, nexthdr);
|
|
skb->h.raw = (u8*)(hdr+1);
|
|
- if (ipv6_parse_hopopts(skb, offsetof(struct ipv6hdr, nexthdr)) < 0) {
|
|
- IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
|
|
- return 0;
|
|
+ if (ipv6_parse_hopopts(&skb, &nhoff) < 0) {
|
|
+ skb = NULL;
|
|
+ goto err;
|
|
}
|
|
- hdr = skb->nh.ipv6h;
|
|
}
|
|
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ if (idev)
|
|
+ in6_dev_put(idev);
|
|
+#endif
|
|
return NF_HOOK(PF_INET6,NF_IP6_PRE_ROUTING, skb, dev, NULL, ip6_rcv_finish);
|
|
truncated:
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INTRUNCATEDPKTS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INTRUNCATEDPKTS);
|
|
+#endif
|
|
err:
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
|
|
+#endif
|
|
drop:
|
|
- kfree_skb(skb);
|
|
+ if (skb)
|
|
+ kfree_skb(skb);
|
|
out:
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ if (idev)
|
|
+ in6_dev_put(idev);
|
|
+#endif
|
|
return 0;
|
|
}
|
|
|
|
@@ -135,17 +161,16 @@
|
|
{
|
|
struct inet6_protocol *ipprot;
|
|
struct sock *raw_sk;
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct dst_entry *dst = skb->dst;
|
|
+ struct inet6_dev *idev = ((struct rt6_info *)dst)->rt6i_idev;
|
|
+#endif
|
|
unsigned int nhoff;
|
|
int nexthdr;
|
|
u8 hash;
|
|
int cksum_sub = 0;
|
|
|
|
skb->h.raw = skb->nh.raw + sizeof(struct ipv6hdr);
|
|
-
|
|
- /*
|
|
- * Parse extension headers
|
|
- */
|
|
-
|
|
nexthdr = skb->nh.ipv6h->nexthdr;
|
|
nhoff = offsetof(struct ipv6hdr, nexthdr);
|
|
|
|
@@ -193,15 +218,27 @@
|
|
if (ret > 0)
|
|
goto resubmit;
|
|
else if (ret == 0)
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INDELIVERS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INDELIVERS);
|
|
+#endif
|
|
} else {
|
|
if (!raw_sk) {
|
|
if (xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INUNKNOWNPROTOS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INUNKNOWNPROTOS);
|
|
+#endif
|
|
icmpv6_param_prob(skb, ICMPV6_UNK_NEXTHDR, nhoff);
|
|
}
|
|
} else {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INDELIVERS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INDELIVERS);
|
|
+#endif
|
|
kfree_skb(skb);
|
|
}
|
|
}
|
|
@@ -209,7 +246,11 @@
|
|
return 0;
|
|
|
|
discard:
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INDISCARDS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
|
|
+#endif
|
|
rcu_read_unlock();
|
|
kfree_skb(skb);
|
|
return 0;
|
|
@@ -224,18 +265,65 @@
|
|
int ip6_mc_input(struct sk_buff *skb)
|
|
{
|
|
struct ipv6hdr *hdr;
|
|
+#if defined(CONFIG_IPV6_STATISTICS) || defined(CONFIG_IPV6_MROUTE)
|
|
+ struct dst_entry *dst = skb->dst;
|
|
+#endif
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct inet6_dev *idev = ((struct rt6_info *)dst)->rt6i_idev;
|
|
+#endif
|
|
int deliver;
|
|
+ int discard = 1;
|
|
|
|
+#ifdef CONFIG_IPV6_MROUTE
|
|
+ skb->dev = dst->dev;
|
|
+#endif
|
|
+
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INMCASTPKTS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INMCASTPKTS);
|
|
+#endif
|
|
|
|
hdr = skb->nh.ipv6h;
|
|
deliver = likely(!(skb->dev->flags & (IFF_PROMISC|IFF_ALLMULTI))) ||
|
|
ipv6_chk_mcast_addr(skb->dev, &hdr->daddr, NULL);
|
|
|
|
+#ifdef CONFIG_IPV6_MROUTE
|
|
/*
|
|
- * IPv6 multicast router mode isnt currently supported.
|
|
+ * IPv6 multicast router mode is now supported ;)
|
|
*/
|
|
+ if (ipv6_devconf.mc_forwarding == 1) {
|
|
+ int addr_typed;
|
|
+ int addr_types;
|
|
+
|
|
+ addr_typed = ipv6_addr_type(&hdr->daddr);
|
|
+ addr_types = ipv6_addr_type(&hdr->saddr);
|
|
+
|
|
+ if (!(addr_typed & (IPV6_ADDR_LOOPBACK | IPV6_ADDR_LINKLOCAL))) {
|
|
+ struct sk_buff *skb2;
|
|
+
|
|
+ /* check if this is a mld message */
|
|
+ if(hdr->nexthdr == NEXTHDR_HOP) {
|
|
+ if(skb->h.raw[0] == IPPROTO_ICMPV6 ){
|
|
+ ip6_input(skb);
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (deliver) {
|
|
+ skb2 = skb_clone(skb,GFP_ATOMIC);
|
|
+ } else {
|
|
+ discard = 0;
|
|
+ skb2 = skb;
|
|
+ }
|
|
+ ip6_mr_input(skb2);
|
|
+ }
|
|
+ }
|
|
+#else
|
|
#if 0
|
|
+ /*
|
|
+ * IPv6 multicast router mode isnt currently supported.
|
|
+ */
|
|
if (ipv6_config.multicast_route) {
|
|
int addr_type;
|
|
|
|
@@ -257,13 +345,14 @@
|
|
}
|
|
}
|
|
#endif
|
|
+#endif
|
|
|
|
if (likely(deliver)) {
|
|
+ discard = 0;
|
|
ip6_input(skb);
|
|
- return 0;
|
|
}
|
|
/* discard */
|
|
- kfree_skb(skb);
|
|
-
|
|
+ if (discard)
|
|
+ kfree_skb(skb);
|
|
return 0;
|
|
}
|
|
diff -urN linux-2.6.11/net/ipv6/ip6_output.c x1/net/ipv6/ip6_output.c
|
|
--- linux-2.6.11/net/ipv6/ip6_output.c 2005-03-02 08:37:47.000000000 +0100
|
|
+++ x1/net/ipv6/ip6_output.c 2005-02-11 17:24:31.000000000 +0100
|
|
@@ -75,6 +75,9 @@
|
|
|
|
struct dst_entry *dst = skb->dst;
|
|
struct hh_cache *hh = dst->hh;
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct inet6_dev *idev = ((struct rt6_info *)dst)->rt6i_idev;
|
|
+#endif
|
|
|
|
if (hh) {
|
|
int hh_alen;
|
|
@@ -88,14 +91,18 @@
|
|
} else if (dst->neighbour)
|
|
return dst->neighbour->output(skb);
|
|
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_OUTNOROUTES);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_OUTNOROUTES);
|
|
+#endif
|
|
kfree_skb(skb);
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
/* dev_loopback_xmit for use with netfilter. */
|
|
-static int ip6_dev_loopback_xmit(struct sk_buff *newskb)
|
|
+int ip6_dev_loopback_xmit(struct sk_buff *newskb)
|
|
{
|
|
newskb->mac.raw = newskb->data;
|
|
__skb_pull(newskb, newskb->nh.raw - newskb->data);
|
|
@@ -112,6 +119,9 @@
|
|
{
|
|
struct dst_entry *dst = skb->dst;
|
|
struct net_device *dev = dst->dev;
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct inet6_dev *idev = ((struct rt6_info *)dst)->rt6i_idev;
|
|
+#endif
|
|
|
|
skb->protocol = htons(ETH_P_IPV6);
|
|
skb->dev = dev;
|
|
@@ -133,13 +143,21 @@
|
|
ip6_dev_loopback_xmit);
|
|
|
|
if (skb->nh.ipv6h->hop_limit == 0) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS);
|
|
+#endif
|
|
kfree_skb(skb);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTMCASTPKTS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTMCASTPKTS);
|
|
+#endif
|
|
}
|
|
|
|
return NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb,NULL, skb->dev,ip6_output_finish);
|
|
@@ -147,7 +165,7 @@
|
|
|
|
int ip6_output(struct sk_buff *skb)
|
|
{
|
|
- if (skb->len > dst_pmtu(skb->dst))
|
|
+ if (skb->len > dst_pmtu(skb->dst) || ip6_dst_allfrag(skb->dst))
|
|
return ip6_fragment(skb, ip6_output2);
|
|
else
|
|
return ip6_output2(skb);
|
|
@@ -166,11 +184,21 @@
|
|
.saddr = iph->saddr, } },
|
|
.proto = iph->nexthdr,
|
|
};
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct inet6_dev *idev = NULL;
|
|
+#endif
|
|
|
|
dst = ip6_route_output(skb->sk, &fl);
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ idev = ((struct rt6_info *)dst)->rt6i_idev;
|
|
+#endif
|
|
|
|
if (dst->error) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTNOROUTES);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTNOROUTES);
|
|
+#endif
|
|
LIMIT_NETDEBUG(
|
|
printk(KERN_DEBUG "ip6_route_me_harder: No more route.\n"));
|
|
dst_release(dst);
|
|
@@ -209,11 +237,16 @@
|
|
struct in6_addr *first_hop = &fl->fl6_dst;
|
|
struct dst_entry *dst = skb->dst;
|
|
struct ipv6hdr *hdr;
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct inet6_dev *idev = ((struct rt6_info *)dst)->rt6i_idev;
|
|
+#endif
|
|
u8 proto = fl->proto;
|
|
int seg_len = skb->len;
|
|
int hlimit;
|
|
u32 mtu;
|
|
|
|
+ skb->h.raw = skb->data;
|
|
+
|
|
if (opt) {
|
|
int head_room;
|
|
|
|
@@ -229,7 +262,11 @@
|
|
kfree_skb(skb);
|
|
skb = skb2;
|
|
if (skb == NULL) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS);
|
|
+#endif
|
|
return -ENOBUFS;
|
|
}
|
|
if (sk)
|
|
@@ -253,6 +290,8 @@
|
|
hlimit = np->hop_limit;
|
|
if (hlimit < 0)
|
|
hlimit = dst_metric(dst, RTAX_HOPLIMIT);
|
|
+ if (hlimit < 0)
|
|
+ hlimit = ipv6_get_hoplimit(dst->dev);
|
|
|
|
hdr->payload_len = htons(seg_len);
|
|
hdr->nexthdr = proto;
|
|
@@ -263,15 +302,28 @@
|
|
|
|
mtu = dst_pmtu(dst);
|
|
if ((skb->len <= mtu) || ipfragok) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS);
|
|
+#endif
|
|
return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute);
|
|
}
|
|
|
|
if (net_ratelimit())
|
|
- printk(KERN_DEBUG "IPv6: sending pkt_too_big to self\n");
|
|
+ printk(KERN_DEBUG "IPv6: sending pkt_too_big to self; "
|
|
+ "saddr=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x, "
|
|
+ "daddr=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x, "
|
|
+ "skb->len(%d) > mtu(%d)\n",
|
|
+ NIP6(hdr->saddr), NIP6(hdr->daddr),
|
|
+ skb->len, mtu);
|
|
skb->dev = dst->dev;
|
|
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev);
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_FRAGFAILS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_FRAGFAILS);
|
|
+#endif
|
|
kfree_skb(skb);
|
|
return -EMSGSIZE;
|
|
}
|
|
@@ -348,14 +400,32 @@
|
|
struct dst_entry *dst = skb->dst;
|
|
struct ipv6hdr *hdr = skb->nh.ipv6h;
|
|
struct inet6_skb_parm *opt = IP6CB(skb);
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct inet6_dev *idev = ((struct rt6_info *)dst)->rt6i_idev;
|
|
+#endif
|
|
|
|
if (ipv6_devconf.forwarding == 0)
|
|
goto error;
|
|
|
|
+#ifdef CONFIG_USE_POLICY_FWD
|
|
if (!xfrm6_policy_check(NULL, XFRM_POLICY_FWD, skb)) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_INDISCARDS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_INDISCARDS);
|
|
+#endif
|
|
goto drop;
|
|
}
|
|
+#else
|
|
+ if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_INDISCARDS);
|
|
+#else
|
|
+ IP6_INC_STATS(IPSTATS_MIB_INDISCARDS);
|
|
+#endif
|
|
+ goto drop;
|
|
+ }
|
|
+#endif
|
|
|
|
skb->ip_summed = CHECKSUM_NONE;
|
|
|
|
@@ -392,7 +462,11 @@
|
|
}
|
|
|
|
if (!xfrm6_route_forward(skb)) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_INDISCARDS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_INDISCARDS);
|
|
+#endif
|
|
goto drop;
|
|
}
|
|
|
|
@@ -430,14 +504,23 @@
|
|
/* Again, force OUTPUT device used as source address */
|
|
skb->dev = dst->dev;
|
|
icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst_pmtu(dst), skb->dev);
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INTOOBIGERRORS);
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_FRAGFAILS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INTOOBIGERRORS);
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_FRAGFAILS);
|
|
+#endif
|
|
kfree_skb(skb);
|
|
return -EMSGSIZE;
|
|
}
|
|
|
|
if (skb_cow(skb, dst->dev->hard_header_len)) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS);
|
|
+#endif
|
|
goto drop;
|
|
}
|
|
|
|
@@ -447,11 +530,19 @@
|
|
|
|
hdr->hop_limit--;
|
|
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_OUTFORWDATAGRAMS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_OUTFORWDATAGRAMS);
|
|
+#endif
|
|
return NF_HOOK(PF_INET6,NF_IP6_FORWARD, skb, skb->dev, dst->dev, ip6_forward_finish);
|
|
|
|
error:
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INADDRERRORS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INADDRERRORS);
|
|
+#endif
|
|
drop:
|
|
kfree_skb(skb);
|
|
return -EINVAL;
|
|
@@ -500,17 +591,21 @@
|
|
switch (**nexthdr) {
|
|
|
|
case NEXTHDR_HOP:
|
|
+ break;
|
|
case NEXTHDR_ROUTING:
|
|
+ found_rhdr = 1;
|
|
+ break;
|
|
case NEXTHDR_DEST:
|
|
- if (**nexthdr == NEXTHDR_ROUTING) found_rhdr = 1;
|
|
- if (**nexthdr == NEXTHDR_DEST && found_rhdr) return offset;
|
|
- offset += ipv6_optlen(exthdr);
|
|
- *nexthdr = &exthdr->nexthdr;
|
|
- exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
|
|
+ if (found_rhdr)
|
|
+ return offset;
|
|
break;
|
|
- default :
|
|
+ default:
|
|
return offset;
|
|
}
|
|
+
|
|
+ offset += ipv6_optlen(exthdr);
|
|
+ *nexthdr = &exthdr->nexthdr;
|
|
+ exthdr = (struct ipv6_opt_hdr*)(skb->nh.raw + offset);
|
|
}
|
|
|
|
return offset;
|
|
@@ -523,6 +618,9 @@
|
|
struct rt6_info *rt = (struct rt6_info*)skb->dst;
|
|
struct ipv6hdr *tmp_hdr;
|
|
struct frag_hdr *fh;
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct inet6_dev *idev = rt->rt6i_idev;
|
|
+#endif
|
|
unsigned int mtu, hlen, left, len;
|
|
u32 frag_id = 0;
|
|
int ptr, offset = 0, err=0;
|
|
@@ -566,7 +664,11 @@
|
|
|
|
tmp_hdr = kmalloc(hlen, GFP_ATOMIC);
|
|
if (!tmp_hdr) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_FRAGFAILS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_FRAGFAILS);
|
|
+#endif
|
|
return -ENOMEM;
|
|
}
|
|
|
|
@@ -622,7 +724,11 @@
|
|
kfree(tmp_hdr);
|
|
|
|
if (err == 0) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_FRAGOKS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_FRAGOKS);
|
|
+#endif
|
|
return 0;
|
|
}
|
|
|
|
@@ -632,7 +738,11 @@
|
|
frag = skb;
|
|
}
|
|
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_FRAGFAILS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_FRAGFAILS);
|
|
+#endif
|
|
return err;
|
|
}
|
|
|
|
@@ -665,7 +775,11 @@
|
|
|
|
if ((frag = alloc_skb(len+hlen+sizeof(struct frag_hdr)+LL_RESERVED_SPACE(rt->u.dst.dev), GFP_ATOMIC)) == NULL) {
|
|
NETDEBUG(printk(KERN_INFO "IPv6: frag: no memory for new fragment!\n"));
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_FRAGFAILS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_FRAGFAILS);
|
|
+#endif
|
|
err = -ENOMEM;
|
|
goto fail;
|
|
}
|
|
@@ -723,19 +837,31 @@
|
|
* Put this fragment into the sending queue.
|
|
*/
|
|
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_FRAGCREATES);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_FRAGCREATES);
|
|
+#endif
|
|
|
|
err = output(frag);
|
|
if (err)
|
|
goto fail;
|
|
}
|
|
kfree_skb(skb);
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_FRAGOKS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_FRAGOKS);
|
|
+#endif
|
|
return err;
|
|
|
|
fail:
|
|
kfree_skb(skb);
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_FRAGFAILS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_FRAGFAILS);
|
|
+#endif
|
|
return err;
|
|
}
|
|
|
|
@@ -814,6 +940,9 @@
|
|
struct inet_sock *inet = inet_sk(sk);
|
|
struct ipv6_pinfo *np = inet6_sk(sk);
|
|
struct sk_buff *skb;
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct inet6_dev *idev = rt->rt6i_idev;
|
|
+#endif
|
|
unsigned int maxfraglen, fragheaderlen;
|
|
int exthdrlen;
|
|
int hh_len;
|
|
@@ -848,6 +977,7 @@
|
|
inet->cork.fl = *fl;
|
|
np->cork.hop_limit = hlimit;
|
|
inet->cork.fragsize = mtu = dst_pmtu(&rt->u.dst);
|
|
+ inet->cork.flags |= ip6_dst_allfrag(&rt->u.dst) ? IPCORK_ALLFRAG : 0;
|
|
inet->cork.length = 0;
|
|
sk->sk_sndmsg_page = NULL;
|
|
sk->sk_sndmsg_off = 0;
|
|
@@ -899,7 +1029,7 @@
|
|
|
|
while (length > 0) {
|
|
/* Check if the remaining data fits into current packet. */
|
|
- copy = mtu - skb->len;
|
|
+ copy = (inet->cork.length <= mtu && !(inet->cork.flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - skb->len;
|
|
if (copy < length)
|
|
copy = maxfraglen - skb->len;
|
|
|
|
@@ -924,7 +1054,7 @@
|
|
* we know we need more fragment(s).
|
|
*/
|
|
datalen = length + fraggap;
|
|
- if (datalen > mtu - fragheaderlen)
|
|
+ if (datalen > (inet->cork.length <= mtu && !(inet->cork.flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen)
|
|
datalen = maxfraglen - fragheaderlen;
|
|
|
|
fraglen = datalen + fragheaderlen;
|
|
@@ -1080,7 +1210,11 @@
|
|
return 0;
|
|
error:
|
|
inet->cork.length -= length;
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS);
|
|
+#endif
|
|
return err;
|
|
}
|
|
|
|
@@ -1095,6 +1229,9 @@
|
|
struct ipv6_txoptions *opt = np->cork.opt;
|
|
struct rt6_info *rt = np->cork.rt;
|
|
struct flowi *fl = &inet->cork.fl;
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct inet6_dev *idev = rt->rt6i_idev;
|
|
+#endif
|
|
unsigned char proto = fl->proto;
|
|
int err = 0;
|
|
|
|
@@ -1140,7 +1277,11 @@
|
|
ipv6_addr_copy(&hdr->daddr, final_dst);
|
|
|
|
skb->dst = dst_clone(&rt->u.dst);
|
|
- IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS);
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
|
|
+#else
|
|
+ IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS);
|
|
+#endif
|
|
err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dst->dev, dst_output);
|
|
if (err) {
|
|
if (err > 0)
|
|
@@ -1158,6 +1299,7 @@
|
|
if (np->cork.rt) {
|
|
dst_release(&np->cork.rt->u.dst);
|
|
np->cork.rt = NULL;
|
|
+ inet->cork.flags &= ~IPCORK_ALLFRAG;
|
|
}
|
|
memset(&inet->cork.fl, 0, sizeof(inet->cork.fl));
|
|
return err;
|
|
@@ -1170,9 +1312,19 @@
|
|
struct inet_sock *inet = inet_sk(sk);
|
|
struct ipv6_pinfo *np = inet6_sk(sk);
|
|
struct sk_buff *skb;
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct dst_entry *dst;
|
|
+ struct inet6_dev *idev = NULL;
|
|
+#endif
|
|
|
|
while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ dst = skb->dst;
|
|
+ idev = ((struct rt6_info *)dst)->rt6i_idev;
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS);
|
|
+#endif
|
|
kfree_skb(skb);
|
|
}
|
|
|
|
@@ -1185,6 +1337,7 @@
|
|
if (np->cork.rt) {
|
|
dst_release(&np->cork.rt->u.dst);
|
|
np->cork.rt = NULL;
|
|
+ inet->cork.flags &= ~IPCORK_ALLFRAG;
|
|
}
|
|
memset(&inet->cork.fl, 0, sizeof(inet->cork.fl));
|
|
}
|
|
diff -urN linux-2.6.11/net/ipv6/ip6_tunnel.c x1/net/ipv6/ip6_tunnel.c
|
|
--- linux-2.6.11/net/ipv6/ip6_tunnel.c 2005-03-02 08:37:48.000000000 +0100
|
|
+++ x1/net/ipv6/ip6_tunnel.c 2005-02-18 10:36:26.000000000 +0100
|
|
@@ -736,7 +736,7 @@
|
|
dsfield = INET_ECN_encapsulate(0, dsfield);
|
|
ipv6_change_dsfield(ipv6h, ~INET_ECN_MASK, dsfield);
|
|
ipv6h->payload_len = htons(skb->len - sizeof(struct ipv6hdr));
|
|
- ipv6h->hop_limit = t->parms.hop_limit;
|
|
+ ipv6h->hop_limit = t->parms.hop_limit; /*XXX use physical link's mtu */
|
|
ipv6h->nexthdr = proto;
|
|
ipv6_addr_copy(&ipv6h->saddr, &fl.fl6_src);
|
|
ipv6_addr_copy(&ipv6h->daddr, &fl.fl6_dst);
|
|
diff -urN linux-2.6.11/net/ipv6/ip6mr.c x1/net/ipv6/ip6mr.c
|
|
--- linux-2.6.11/net/ipv6/ip6mr.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/net/ipv6/ip6mr.c 2005-02-09 16:31:39.000000000 +0100
|
|
@@ -0,0 +1,1682 @@
|
|
+/*
|
|
+ * Linux IPv6 multicast routing support for BSD pim6sd
|
|
+ *
|
|
+ * (c) 2004 Mickael Hoerdt, <hoerdt@clarinet.u-strasbg.fr>
|
|
+ * LSIIT Laboratory, Strasbourg, France
|
|
+ * (c) 2004 Jean-Philippe Andriot, <jean-philippe.andriot@6WIND.com>
|
|
+ * 6WIND, Paris, France
|
|
+ *
|
|
+ * 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.
|
|
+ *
|
|
+ * Version: $Id: ip6mr.c,v 1.1 2004/12/27 17:54:01 hoerdt Exp $
|
|
+ *
|
|
+ * Fixes:
|
|
+ * Michael Chastain : Incorrect size of copying.
|
|
+ * Alan Cox : Added the cache manager code
|
|
+ * Alan Cox : Fixed the clone/copy bug and device race.
|
|
+ * Mike McLagan : Routing by source
|
|
+ * Malcolm Beattie : Buffer handling fixes.
|
|
+ * Alexey Kuznetsov : Double buffer free and other fixes.
|
|
+ * SVR Anand : Fixed several multicast bugs and problems.
|
|
+ * Alexey Kuznetsov : Status, optimisations and more.
|
|
+ * Brad Parker : Better behaviour on mrouted upcall
|
|
+ * overflow.
|
|
+ * Carlos Picoto : PIMv1 Support
|
|
+ * Pavlin Ivanov Radoslavov: PIMv2 Registers must checksum only PIM header
|
|
+ * Relax this requirement to work with older peers.
|
|
+ * Mickael Hoerdt and : IPv6 support based on linux/net/ipv4/ipmr.c [Linux 2.x]
|
|
+ * Jean-Philippe Andriot on netinet/ip6_mroute.c [*BSD]
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include <linux/config.h>
|
|
+#include <asm/system.h>
|
|
+#include <asm/uaccess.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/timer.h>
|
|
+#include <linux/mm.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/fcntl.h>
|
|
+#include <linux/stat.h>
|
|
+#include <linux/socket.h>
|
|
+#include <linux/in.h>
|
|
+#include <linux/inet.h>
|
|
+#include <linux/netdevice.h>
|
|
+#include <linux/inetdevice.h>
|
|
+#include <linux/igmp.h>
|
|
+#include <linux/proc_fs.h>
|
|
+#include <linux/seq_file.h>
|
|
+#include <linux/mroute.h>
|
|
+#include <linux/init.h>
|
|
+#include <net/ip.h>
|
|
+#include <net/protocol.h>
|
|
+#include <linux/skbuff.h>
|
|
+#include <net/sock.h>
|
|
+#include <net/icmp.h>
|
|
+#include <net/udp.h>
|
|
+#include <net/raw.h>
|
|
+#include <linux/notifier.h>
|
|
+#include <linux/if_arp.h>
|
|
+#include <linux/netfilter_ipv4.h>
|
|
+#include <net/ipip.h>
|
|
+#include <net/checksum.h>
|
|
+
|
|
+
|
|
+#include <net/ipv6.h>
|
|
+#include <net/ip6_route.h>
|
|
+#include <linux/mroute6.h>
|
|
+#include <net/addrconf.h>
|
|
+#include <linux/netfilter_ipv6.h>
|
|
+
|
|
+struct sock *mroute6_socket;
|
|
+
|
|
+
|
|
+/* Big lock, protecting vif table, mrt cache and mroute socket state.
|
|
+ Note that the changes are semaphored via rtnl_lock.
|
|
+ */
|
|
+
|
|
+static rwlock_t mrt_lock = RW_LOCK_UNLOCKED;
|
|
+
|
|
+/*
|
|
+ * Multicast router control variables
|
|
+ */
|
|
+
|
|
+static struct mif_device vif6_table[MAXMIFS]; /* Devices */
|
|
+static int maxvif;
|
|
+
|
|
+#define MIF_EXISTS(idx) (vif6_table[idx].dev != NULL)
|
|
+
|
|
+static int mroute_do_assert; /* Set in PIM assert */
|
|
+static int mroute_do_pim;
|
|
+
|
|
+static struct mfc6_cache *mfc6_cache_array[MFC_LINES]; /* Forwarding cache */
|
|
+
|
|
+static struct mfc6_cache *mfc_unres_queue; /* Queue of unresolved entries */
|
|
+static atomic_t cache_resolve_queue_len; /* Size of unresolved */
|
|
+
|
|
+/* Special spinlock for queue of unresolved entries */
|
|
+static spinlock_t mfc_unres_lock = SPIN_LOCK_UNLOCKED;
|
|
+
|
|
+/* We return to original Alan's scheme. Hash table of resolved
|
|
+ entries is changed only in process context and protected
|
|
+ with weak lock mrt_lock. Queue of unresolved entries is protected
|
|
+ with strong spinlock mfc_unres_lock.
|
|
+
|
|
+ In this case data path is free of exclusive locks at all.
|
|
+ */
|
|
+
|
|
+static kmem_cache_t *mrt_cachep;
|
|
+
|
|
+static int ip6_mr_forward(struct sk_buff *skb, struct mfc6_cache *cache, int local);
|
|
+static int ip6mr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert);
|
|
+static int ip6mr_fill_mroute(struct sk_buff *skb, struct mfc6_cache *c, struct rtmsg *rtm);
|
|
+
|
|
+static struct inet6_protocol pim6_protocol;
|
|
+
|
|
+static struct timer_list ipmr_expire_timer;
|
|
+
|
|
+
|
|
+#ifdef CONFIG_PROC_FS
|
|
+
|
|
+struct ipmr_mfc_iter {
|
|
+ struct mfc6_cache **cache;
|
|
+ int ct;
|
|
+};
|
|
+
|
|
+
|
|
+static struct mfc6_cache *ipmr_mfc_seq_idx(struct ipmr_mfc_iter *it, loff_t pos)
|
|
+{
|
|
+ struct mfc6_cache *mfc;
|
|
+
|
|
+ it->cache = mfc6_cache_array;
|
|
+ read_lock(&mrt_lock);
|
|
+ for (it->ct = 0; it->ct < MFC_LINES; it->ct++)
|
|
+ for(mfc = mfc6_cache_array[it->ct]; mfc; mfc = mfc->next)
|
|
+ if (pos-- == 0)
|
|
+ return mfc;
|
|
+ read_unlock(&mrt_lock);
|
|
+
|
|
+ it->cache = &mfc_unres_queue;
|
|
+ spin_lock_bh(&mfc_unres_lock);
|
|
+ for(mfc = mfc_unres_queue; mfc; mfc = mfc->next)
|
|
+ if (pos-- == 0)
|
|
+ return mfc;
|
|
+ spin_unlock_bh(&mfc_unres_lock);
|
|
+
|
|
+ it->cache = NULL;
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+/*
|
|
+ * The /proc interfaces to multicast routing /proc/ip6_mr_cache /proc/ip6_mr_vif
|
|
+ */
|
|
+
|
|
+struct ipmr_vif_iter {
|
|
+ int ct;
|
|
+};
|
|
+
|
|
+static struct mif_device *ip6mr_vif_seq_idx(struct ipmr_vif_iter *iter,
|
|
+ loff_t pos)
|
|
+{
|
|
+ for (iter->ct = 0; iter->ct < maxvif; ++iter->ct) {
|
|
+ if(!MIF_EXISTS(iter->ct))
|
|
+ continue;
|
|
+ if (pos-- == 0)
|
|
+ return &vif6_table[iter->ct];
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void *ip6mr_vif_seq_start(struct seq_file *seq, loff_t *pos)
|
|
+{
|
|
+ read_lock(&mrt_lock);
|
|
+ return *pos ? ip6mr_vif_seq_idx(seq->private, *pos - 1)
|
|
+ : SEQ_START_TOKEN;
|
|
+}
|
|
+
|
|
+static void *ip6mr_vif_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
|
+{
|
|
+ struct ipmr_vif_iter *iter = seq->private;
|
|
+
|
|
+ ++*pos;
|
|
+ if (v == SEQ_START_TOKEN)
|
|
+ return ip6mr_vif_seq_idx(iter, 0);
|
|
+
|
|
+ while (++iter->ct < maxvif) {
|
|
+ if(!MIF_EXISTS(iter->ct))
|
|
+ continue;
|
|
+ return &vif6_table[iter->ct];
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void ip6mr_vif_seq_stop(struct seq_file *seq, void *v)
|
|
+{
|
|
+ read_unlock(&mrt_lock);
|
|
+}
|
|
+
|
|
+static int ip6mr_vif_seq_show(struct seq_file *seq, void *v)
|
|
+{
|
|
+ if (v == SEQ_START_TOKEN) {
|
|
+ seq_puts(seq,
|
|
+ "Interface BytesIn PktsIn BytesOut PktsOut Flags\n");
|
|
+ } else {
|
|
+ const struct mif_device *vif = v;
|
|
+ const char *name = vif->dev ? vif->dev->name : "none";
|
|
+
|
|
+ seq_printf(seq,
|
|
+ "%2Zd %-10s %8ld %7ld %8ld %7ld %05X\n",
|
|
+ vif - vif6_table,
|
|
+ name, vif->bytes_in, vif->pkt_in,
|
|
+ vif->bytes_out, vif->pkt_out,
|
|
+ vif->flags);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct seq_operations ip6mr_vif_seq_ops = {
|
|
+ .start = ip6mr_vif_seq_start,
|
|
+ .next = ip6mr_vif_seq_next,
|
|
+ .stop = ip6mr_vif_seq_stop,
|
|
+ .show = ip6mr_vif_seq_show,
|
|
+};
|
|
+
|
|
+static int ip6mr_vif_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ struct seq_file *seq;
|
|
+ int rc = -ENOMEM;
|
|
+ struct ipmr_vif_iter *s = kmalloc(sizeof(*s), GFP_KERNEL);
|
|
+
|
|
+ if (!s)
|
|
+ goto out;
|
|
+
|
|
+ rc = seq_open(file, &ip6mr_vif_seq_ops);
|
|
+ if (rc)
|
|
+ goto out_kfree;
|
|
+
|
|
+ s->ct = 0;
|
|
+ seq = file->private_data;
|
|
+ seq->private = s;
|
|
+out:
|
|
+ return rc;
|
|
+out_kfree:
|
|
+ kfree(s);
|
|
+ goto out;
|
|
+
|
|
+}
|
|
+
|
|
+static struct file_operations ip6mr_vif_fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .open = ip6mr_vif_open,
|
|
+ .read = seq_read,
|
|
+ .llseek = seq_lseek,
|
|
+ .release = seq_release,
|
|
+};
|
|
+
|
|
+static void *ipmr_mfc_seq_start(struct seq_file *seq, loff_t *pos)
|
|
+{
|
|
+ return *pos ? ipmr_mfc_seq_idx(seq->private, *pos - 1)
|
|
+ : SEQ_START_TOKEN;
|
|
+}
|
|
+
|
|
+static void *ipmr_mfc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
|
+{
|
|
+ struct mfc6_cache *mfc = v;
|
|
+ struct ipmr_mfc_iter *it = seq->private;
|
|
+
|
|
+ ++*pos;
|
|
+
|
|
+ if (v == SEQ_START_TOKEN)
|
|
+ return ipmr_mfc_seq_idx(seq->private, 0);
|
|
+
|
|
+ if (mfc->next)
|
|
+ return mfc->next;
|
|
+
|
|
+ if (it->cache == &mfc_unres_queue)
|
|
+ goto end_of_list;
|
|
+
|
|
+ BUG_ON(it->cache != mfc6_cache_array);
|
|
+
|
|
+ while (++it->ct < MFC_LINES) {
|
|
+ mfc = mfc6_cache_array[it->ct];
|
|
+ if (mfc)
|
|
+ return mfc;
|
|
+ }
|
|
+
|
|
+ /* exhausted cache_array, show unresolved */
|
|
+ read_unlock(&mrt_lock);
|
|
+ it->cache = &mfc_unres_queue;
|
|
+ it->ct = 0;
|
|
+
|
|
+ spin_lock_bh(&mfc_unres_lock);
|
|
+ mfc = mfc_unres_queue;
|
|
+ if (mfc)
|
|
+ return mfc;
|
|
+
|
|
+ end_of_list:
|
|
+ spin_unlock_bh(&mfc_unres_lock);
|
|
+ it->cache = NULL;
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void ipmr_mfc_seq_stop(struct seq_file *seq, void *v)
|
|
+{
|
|
+ struct ipmr_mfc_iter *it = seq->private;
|
|
+
|
|
+ if (it->cache == &mfc_unres_queue)
|
|
+ spin_unlock_bh(&mfc_unres_lock);
|
|
+ else if (it->cache == mfc6_cache_array)
|
|
+ read_unlock(&mrt_lock);
|
|
+}
|
|
+
|
|
+static int ipmr_mfc_seq_show(struct seq_file *seq, void *v)
|
|
+{
|
|
+ int n;
|
|
+
|
|
+ if (v == SEQ_START_TOKEN) {
|
|
+ seq_puts(seq,
|
|
+ "Group Origin Iif Pkts Bytes Wrong Oifs\n");
|
|
+ } else {
|
|
+ const struct mfc6_cache *mfc = v;
|
|
+ const struct ipmr_mfc_iter *it = seq->private;
|
|
+ int i;
|
|
+
|
|
+ for(i=0;i<16;i++) {
|
|
+ seq_printf(seq,"%02x",mfc->mf6c_mcastgrp.s6_addr[i]);
|
|
+ }
|
|
+ seq_printf(seq," ");
|
|
+ for(i=0;i<16;i++) {
|
|
+ seq_printf(seq,"%02x",mfc->mf6c_origin.s6_addr[i]);
|
|
+ }
|
|
+ seq_printf(seq," ");
|
|
+
|
|
+ seq_printf(seq, "%-3d %8ld %8ld %8ld",
|
|
+ mfc->mf6c_parent,
|
|
+ mfc->mfc_un.res.pkt,
|
|
+ mfc->mfc_un.res.bytes,
|
|
+ mfc->mfc_un.res.wrong_if);
|
|
+
|
|
+ if (it->cache != &mfc_unres_queue) {
|
|
+ for(n = mfc->mfc_un.res.minvif;
|
|
+ n < mfc->mfc_un.res.maxvif; n++ ) {
|
|
+ if(MIF_EXISTS(n)
|
|
+ && mfc->mfc_un.res.ttls[n] < 255)
|
|
+ seq_printf(seq,
|
|
+ " %2d:%-3d",
|
|
+ n, mfc->mfc_un.res.ttls[n]);
|
|
+ }
|
|
+ }
|
|
+ seq_putc(seq, '\n');
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct seq_operations ipmr_mfc_seq_ops = {
|
|
+ .start = ipmr_mfc_seq_start,
|
|
+ .next = ipmr_mfc_seq_next,
|
|
+ .stop = ipmr_mfc_seq_stop,
|
|
+ .show = ipmr_mfc_seq_show,
|
|
+};
|
|
+
|
|
+static int ipmr_mfc_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ struct seq_file *seq;
|
|
+ int rc = -ENOMEM;
|
|
+ struct ipmr_mfc_iter *s = kmalloc(sizeof(*s), GFP_KERNEL);
|
|
+
|
|
+ if (!s)
|
|
+ goto out;
|
|
+
|
|
+ rc = seq_open(file, &ipmr_mfc_seq_ops);
|
|
+ if (rc)
|
|
+ goto out_kfree;
|
|
+
|
|
+ memset(s, 0, sizeof(*s));
|
|
+ seq = file->private_data;
|
|
+ seq->private = s;
|
|
+out:
|
|
+ return rc;
|
|
+out_kfree:
|
|
+ kfree(s);
|
|
+ goto out;
|
|
+
|
|
+}
|
|
+
|
|
+static struct file_operations ip6mr_mfc_fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .open = ipmr_mfc_open,
|
|
+ .read = seq_read,
|
|
+ .llseek = seq_lseek,
|
|
+ .release = seq_release,
|
|
+};
|
|
+#endif
|
|
+
|
|
+#ifdef CONFIG_IPV6_PIMSM_V2
|
|
+static int reg_vif_num = -1;
|
|
+
|
|
+static int pim6_rcv(struct sk_buff **pskb,unsigned int *nhoffp)
|
|
+{
|
|
+ struct pimreghdr *pim;
|
|
+ struct ipv6hdr *encap;
|
|
+ struct sk_buff *skb = *pskb;
|
|
+ struct net_device *reg_dev = NULL;
|
|
+
|
|
+ if (!pskb_may_pull(skb, sizeof(*pim) + sizeof(*encap)))
|
|
+ goto drop;
|
|
+
|
|
+ pim = (struct pimreghdr*)skb->h.raw;
|
|
+ if (pim->type != ((PIM_VERSION<<4)|(PIM_REGISTER)) ||
|
|
+ (pim->flags&PIM_NULL_REGISTER) ||
|
|
+ (ip_compute_csum((void *)pim, sizeof(*pim)) != 0 &&
|
|
+ (u16)csum_fold(skb_checksum(skb, 0, skb->len, 0))))
|
|
+ goto drop;
|
|
+
|
|
+ /* check if the inner packet is destined to mcast group */
|
|
+ encap = (struct ipv6hdr*)(skb->h.raw + sizeof(struct pimreghdr));
|
|
+
|
|
+ if(!(ipv6_addr_type(&encap->daddr)&IPV6_ADDR_MULTICAST) ||
|
|
+ encap->payload_len == 0 ||
|
|
+ ntohs(encap->payload_len) + sizeof(*pim) > skb->len)
|
|
+ goto drop;
|
|
+
|
|
+ read_lock(&mrt_lock);
|
|
+ if (reg_vif_num >= 0)
|
|
+ reg_dev = vif6_table[reg_vif_num].dev;
|
|
+ if (reg_dev)
|
|
+ dev_hold(reg_dev);
|
|
+ read_unlock(&mrt_lock);
|
|
+
|
|
+ if (reg_dev == NULL)
|
|
+ goto drop;
|
|
+
|
|
+ skb->mac.raw = skb->nh.raw;
|
|
+ skb_pull(skb, (u8*)encap - skb->data);
|
|
+ skb->nh.ipv6h = (struct ipv6hdr *)skb->data;
|
|
+ skb->dev = reg_dev;
|
|
+ skb->protocol = htons(ETH_P_IP);
|
|
+ skb->ip_summed = 0;
|
|
+ skb->pkt_type = PACKET_HOST;
|
|
+ dst_release(skb->dst);
|
|
+ ((struct net_device_stats*)reg_dev->priv)->rx_bytes += skb->len;
|
|
+ ((struct net_device_stats*)reg_dev->priv)->rx_packets++;
|
|
+ skb->dst = NULL;
|
|
+#ifdef CONFIG_NETFILTER
|
|
+ nf_conntrack_put(skb->nfct);
|
|
+ skb->nfct = NULL;
|
|
+#endif
|
|
+ netif_rx(skb);
|
|
+ dev_put(reg_dev);
|
|
+ return 0;
|
|
+ drop:
|
|
+ kfree_skb(skb);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct inet6_protocol pim6_protocol = {
|
|
+ .handler = pim6_rcv,
|
|
+};
|
|
+#endif
|
|
+
|
|
+/* Service routines creating virtual interfaces: PIMREG */
|
|
+#ifdef CONFIG_IPV6_PIMSM_V2
|
|
+
|
|
+
|
|
+static int reg_vif_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
+{
|
|
+ read_lock(&mrt_lock);
|
|
+ ((struct net_device_stats*)dev->priv)->tx_bytes += skb->len;
|
|
+ ((struct net_device_stats*)dev->priv)->tx_packets++;
|
|
+ ip6mr_cache_report(skb, reg_vif_num, MRT6MSG_WHOLEPKT);
|
|
+ read_unlock(&mrt_lock);
|
|
+ kfree_skb(skb);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct net_device_stats *reg_vif_get_stats(struct net_device *dev)
|
|
+{
|
|
+ return (struct net_device_stats*)dev->priv;
|
|
+}
|
|
+
|
|
+static void reg_vif_setup(struct net_device *dev)
|
|
+{
|
|
+ dev->type = ARPHRD_PIMREG;
|
|
+ dev->mtu = 1500 - sizeof(struct ipv6hdr) - 8;
|
|
+ dev->flags = IFF_NOARP;
|
|
+ dev->hard_start_xmit = reg_vif_xmit;
|
|
+ dev->get_stats = reg_vif_get_stats;
|
|
+ dev->destructor = free_netdev;
|
|
+}
|
|
+
|
|
+static struct net_device *ip6mr_reg_vif(void)
|
|
+{
|
|
+ struct net_device *dev;
|
|
+ struct inet6_dev *in_dev;
|
|
+
|
|
+ dev = alloc_netdev(sizeof(struct net_device_stats), "pim6reg",
|
|
+ reg_vif_setup);
|
|
+
|
|
+ if (dev == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ if (register_netdevice(dev)) {
|
|
+ free_netdev(dev);
|
|
+ return NULL;
|
|
+ }
|
|
+ dev->iflink = 0;
|
|
+
|
|
+ if ((in_dev = ipv6_find_idev(dev)) == NULL) {
|
|
+ goto failure;
|
|
+ }
|
|
+
|
|
+/*
|
|
+ * if ((in_dev = __in6_dev_get(dev)) == NULL)
|
|
+ goto failure;
|
|
+*/
|
|
+#if 0
|
|
+ in_dev->cnf.rp_filter = 0;
|
|
+#endif
|
|
+
|
|
+ if (dev_open(dev))
|
|
+ goto failure;
|
|
+
|
|
+ return dev;
|
|
+
|
|
+failure:
|
|
+ /* allow the register to be completed before unregistering. */
|
|
+ rtnl_unlock();
|
|
+ rtnl_lock();
|
|
+
|
|
+ unregister_netdevice(dev);
|
|
+ return NULL;
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * Delete a VIF entry
|
|
+ */
|
|
+
|
|
+static int mif6_delete(int vifi)
|
|
+{
|
|
+ struct mif_device *v;
|
|
+ struct net_device *dev;
|
|
+ struct inet6_dev *in_dev;
|
|
+
|
|
+ if (vifi < 0 || vifi >= maxvif)
|
|
+ return -EADDRNOTAVAIL;
|
|
+
|
|
+ v = &vif6_table[vifi];
|
|
+
|
|
+ write_lock_bh(&mrt_lock);
|
|
+ dev = v->dev;
|
|
+ v->dev = NULL;
|
|
+
|
|
+ if (!dev) {
|
|
+ write_unlock_bh(&mrt_lock);
|
|
+ return -EADDRNOTAVAIL;
|
|
+ }
|
|
+
|
|
+#ifdef CONFIG_IPV6_PIMSM_V2
|
|
+ if (vifi == reg_vif_num)
|
|
+ reg_vif_num = -1;
|
|
+#endif
|
|
+
|
|
+ if (vifi+1 == maxvif) {
|
|
+ int tmp;
|
|
+ for (tmp=vifi-1; tmp>=0; tmp--) {
|
|
+ if (MIF_EXISTS(tmp))
|
|
+ break;
|
|
+ }
|
|
+ maxvif = tmp+1;
|
|
+ }
|
|
+
|
|
+ write_unlock_bh(&mrt_lock);
|
|
+
|
|
+ dev_set_allmulti(dev, -1);
|
|
+
|
|
+ if ((in_dev = __in6_dev_get(dev)) != NULL) {
|
|
+ in_dev->cnf.mc_forwarding--;
|
|
+ }
|
|
+
|
|
+ if (v->flags&(MIFF_REGISTER))
|
|
+ unregister_netdevice(dev);
|
|
+
|
|
+ dev_put(dev);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Destroy an unresolved cache entry, killing queued skbs
|
|
+ and reporting error to netlink readers.
|
|
+ */
|
|
+
|
|
+static void ip6mr_destroy_unres(struct mfc6_cache *c)
|
|
+{
|
|
+ struct sk_buff *skb;
|
|
+
|
|
+ atomic_dec(&cache_resolve_queue_len);
|
|
+
|
|
+ while((skb=skb_dequeue(&c->mfc_un.unres.unresolved))) {
|
|
+ if (skb->nh.ipv6h->version == 0) {
|
|
+ struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct ipv6hdr));
|
|
+ nlh->nlmsg_type = NLMSG_ERROR;
|
|
+ nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
|
|
+ skb_trim(skb, nlh->nlmsg_len);
|
|
+ ((struct nlmsgerr*)NLMSG_DATA(nlh))->error = -ETIMEDOUT;
|
|
+ netlink_unicast(rtnl, skb, NETLINK_CB(skb).dst_pid, MSG_DONTWAIT);
|
|
+ } else
|
|
+ kfree_skb(skb);
|
|
+ }
|
|
+
|
|
+ kmem_cache_free(mrt_cachep, c);
|
|
+}
|
|
+
|
|
+
|
|
+/* Single timer process for all the unresolved queue. */
|
|
+
|
|
+static void ipmr_expire_process(unsigned long dummy)
|
|
+{
|
|
+ unsigned long now;
|
|
+ unsigned long expires;
|
|
+ struct mfc6_cache *c, **cp;
|
|
+
|
|
+ if (!spin_trylock(&mfc_unres_lock)) {
|
|
+ mod_timer(&ipmr_expire_timer, jiffies+HZ/10);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (atomic_read(&cache_resolve_queue_len) == 0)
|
|
+ goto out;
|
|
+
|
|
+ now = jiffies;
|
|
+ expires = 10*HZ;
|
|
+ cp = &mfc_unres_queue;
|
|
+
|
|
+ while ((c=*cp) != NULL) {
|
|
+ if (time_after(c->mfc_un.unres.expires, now)) {
|
|
+ unsigned long interval = c->mfc_un.unres.expires - now;
|
|
+ if (interval < expires)
|
|
+ expires = interval;
|
|
+ cp = &c->next;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ *cp = c->next;
|
|
+
|
|
+ ip6mr_destroy_unres(c);
|
|
+ }
|
|
+
|
|
+ if (atomic_read(&cache_resolve_queue_len))
|
|
+ mod_timer(&ipmr_expire_timer, jiffies + expires);
|
|
+
|
|
+out:
|
|
+ spin_unlock(&mfc_unres_lock);
|
|
+}
|
|
+
|
|
+/* Fill oifs list. It is called under write locked mrt_lock. */
|
|
+
|
|
+static void ip6mr_update_threshoulds(struct mfc6_cache *cache, unsigned char *ttls)
|
|
+{
|
|
+ int vifi;
|
|
+
|
|
+ cache->mfc_un.res.minvif = MAXVIFS;
|
|
+ cache->mfc_un.res.maxvif = 0;
|
|
+ memset(cache->mfc_un.res.ttls, 255, MAXVIFS);
|
|
+
|
|
+ for (vifi=0; vifi<maxvif; vifi++) {
|
|
+ if (MIF_EXISTS(vifi) && ttls[vifi] && ttls[vifi] < 255) {
|
|
+ cache->mfc_un.res.ttls[vifi] = ttls[vifi];
|
|
+ if (cache->mfc_un.res.minvif > vifi)
|
|
+ cache->mfc_un.res.minvif = vifi;
|
|
+ if (cache->mfc_un.res.maxvif <= vifi)
|
|
+ cache->mfc_un.res.maxvif = vifi + 1;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static int mif6_add(struct mif6ctl *vifc, int mrtsock)
|
|
+{
|
|
+ int vifi = vifc->mif6c_mifi;
|
|
+ struct mif_device *v = &vif6_table[vifi];
|
|
+ struct net_device *dev;
|
|
+ struct inet6_dev *in_dev;
|
|
+
|
|
+ /* Is vif busy ? */
|
|
+ if (MIF_EXISTS(vifi))
|
|
+ return -EADDRINUSE;
|
|
+
|
|
+ switch (vifc->mif6c_flags) {
|
|
+#ifdef CONFIG_IPV6_PIMSM_V2
|
|
+ case MIFF_REGISTER:
|
|
+ /*
|
|
+ * Special Purpose VIF in PIM
|
|
+ * All the packets will be sent to the daemon
|
|
+ */
|
|
+ if (reg_vif_num >= 0)
|
|
+ return -EADDRINUSE;
|
|
+ dev = ip6mr_reg_vif();
|
|
+ if (!dev)
|
|
+ return -ENOBUFS;
|
|
+ break;
|
|
+#endif
|
|
+ case 0:
|
|
+ dev=dev_get_by_index(vifc->mif6c_pifi);
|
|
+ if (!dev)
|
|
+ return -EADDRNOTAVAIL;
|
|
+ __dev_put(dev);
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if ((in_dev = __in6_dev_get(dev)) == NULL)
|
|
+ return -EADDRNOTAVAIL;
|
|
+ in_dev->cnf.mc_forwarding++;
|
|
+ dev_set_allmulti(dev, +1);
|
|
+
|
|
+ /*
|
|
+ * Fill in the VIF structures
|
|
+ */
|
|
+ v->rate_limit=vifc->vifc_rate_limit;
|
|
+ v->flags=vifc->mif6c_flags;
|
|
+ if(!mrtsock)
|
|
+ v->flags |= VIFF_STATIC;
|
|
+ v->threshold=vifc->vifc_threshold;
|
|
+ v->bytes_in = 0;
|
|
+ v->bytes_out = 0;
|
|
+ v->pkt_in = 0;
|
|
+ v->pkt_out = 0;
|
|
+ v->link = dev->ifindex;
|
|
+ if (v->flags&(MIFF_REGISTER))
|
|
+ v->link = dev->iflink;
|
|
+
|
|
+ /* And finish update writing critical data */
|
|
+ write_lock_bh(&mrt_lock);
|
|
+ dev_hold(dev);
|
|
+ v->dev=dev;
|
|
+#ifdef CONFIG_IPV6_PIMSM_V2
|
|
+ if (v->flags&MIFF_REGISTER)
|
|
+ reg_vif_num = vifi;
|
|
+#endif
|
|
+ if (vifi+1 > maxvif)
|
|
+ maxvif = vifi+1;
|
|
+ write_unlock_bh(&mrt_lock);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static struct mfc6_cache *ip6mr_cache_find(struct in6_addr origin,struct in6_addr mcastgrp)
|
|
+{
|
|
+ int line=MFC6_HASH(mcastgrp,origin);
|
|
+ struct mfc6_cache *c;
|
|
+
|
|
+ for (c=mfc6_cache_array[line]; c; c = c->next) {
|
|
+ if (IN6_ARE_ADDR_EQUAL(&c->mf6c_origin,&origin) &&
|
|
+ IN6_ARE_ADDR_EQUAL(&c->mf6c_mcastgrp,&mcastgrp))
|
|
+ break;
|
|
+ }
|
|
+ return c;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Allocate a multicast cache entry
|
|
+ */
|
|
+static struct mfc6_cache *ip6mr_cache_alloc(void)
|
|
+{
|
|
+ struct mfc6_cache *c=kmem_cache_alloc(mrt_cachep, GFP_KERNEL);
|
|
+ if(c==NULL)
|
|
+ return NULL;
|
|
+ memset(c, 0, sizeof(*c));
|
|
+ c->mfc_un.res.minvif = MAXVIFS;
|
|
+ return c;
|
|
+}
|
|
+
|
|
+static struct mfc6_cache *ip6mr_cache_alloc_unres(void)
|
|
+{
|
|
+ struct mfc6_cache *c=kmem_cache_alloc(mrt_cachep, GFP_ATOMIC);
|
|
+ if(c==NULL)
|
|
+ return NULL;
|
|
+ memset(c, 0, sizeof(*c));
|
|
+ skb_queue_head_init(&c->mfc_un.unres.unresolved);
|
|
+ c->mfc_un.unres.expires = jiffies + 10*HZ;
|
|
+ return c;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * A cache entry has gone into a resolved state from queued
|
|
+ */
|
|
+
|
|
+static void ip6mr_cache_resolve(struct mfc6_cache *uc, struct mfc6_cache *c)
|
|
+{
|
|
+ struct sk_buff *skb;
|
|
+
|
|
+ /*
|
|
+ * Play the pending entries through our router
|
|
+ */
|
|
+
|
|
+ while((skb=__skb_dequeue(&uc->mfc_un.unres.unresolved))) {
|
|
+ if (skb->nh.ipv6h->version == 0) {
|
|
+ int err;
|
|
+ struct nlmsghdr *nlh = (struct nlmsghdr *)skb_pull(skb, sizeof(struct ipv6hdr));
|
|
+
|
|
+ if (ip6mr_fill_mroute(skb, c, NLMSG_DATA(nlh)) > 0) {
|
|
+ nlh->nlmsg_len = skb->tail - (u8*)nlh;
|
|
+ } else {
|
|
+ nlh->nlmsg_type = NLMSG_ERROR;
|
|
+ nlh->nlmsg_len = NLMSG_LENGTH(sizeof(struct nlmsgerr));
|
|
+ skb_trim(skb, nlh->nlmsg_len);
|
|
+ ((struct nlmsgerr*)NLMSG_DATA(nlh))->error = -EMSGSIZE;
|
|
+ }
|
|
+ err = netlink_unicast(rtnl, skb, NETLINK_CB(skb).dst_pid, MSG_DONTWAIT);
|
|
+ } else
|
|
+ ip6_mr_forward(skb, c, 0);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Bounce a cache query up to pim6sd. We could use netlink for this but pim6sd
|
|
+ * expects the following bizarre scheme.
|
|
+ *
|
|
+ * Called under mrt_lock.
|
|
+ */
|
|
+
|
|
+static int ip6mr_cache_report(struct sk_buff *pkt, vifi_t vifi, int assert)
|
|
+{
|
|
+ struct sk_buff *skb;
|
|
+ struct mrt6msg *msg;
|
|
+ int ret;
|
|
+
|
|
+#ifdef CONFIG_IPV6_PIMSM_V2
|
|
+ if (assert == MRT6MSG_WHOLEPKT)
|
|
+ skb = skb_realloc_headroom(pkt, sizeof(struct ipv6hdr));
|
|
+ else
|
|
+#endif
|
|
+ skb = alloc_skb(128, GFP_ATOMIC);
|
|
+
|
|
+ if(!skb)
|
|
+ return -ENOBUFS;
|
|
+
|
|
+ /* I suppose that internal messages
|
|
+ * do not require checksums */
|
|
+
|
|
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
|
|
+
|
|
+#ifdef CONFIG_IPV6_PIMSM_V2
|
|
+ if (assert == MRT6MSG_WHOLEPKT) {
|
|
+ /* Ugly, but we have no choice with this interface.
|
|
+ Duplicate old header, fix length etc.
|
|
+ And all this only to mangle msg->im6_msgtype and
|
|
+ to set msg->im6_mbz to "mbz" :-)
|
|
+ */
|
|
+ msg = (struct mrt6msg*)skb_push(skb, sizeof(struct ipv6hdr));
|
|
+ skb->nh.raw = skb->h.raw = (u8*)msg;
|
|
+ memcpy(msg, pkt->nh.raw, sizeof(struct ipv6hdr));
|
|
+ msg->im6_msgtype = MRT6MSG_WHOLEPKT;
|
|
+ msg->im6_mbz = 0;
|
|
+ msg->im6_mif = reg_vif_num;
|
|
+ } else
|
|
+#endif
|
|
+ {
|
|
+
|
|
+ /*
|
|
+ * Copy the IP header
|
|
+ */
|
|
+
|
|
+ skb->nh.ipv6h = (struct ipv6hdr *)skb_put(skb, sizeof(struct ipv6hdr));
|
|
+ memcpy(skb->data,pkt->data,sizeof(struct ipv6hdr));
|
|
+
|
|
+ msg = (struct mrt6msg*)skb->nh.ipv6h;
|
|
+ skb->dst = dst_clone(pkt->dst);
|
|
+
|
|
+ /*
|
|
+ * Add our header
|
|
+ */
|
|
+
|
|
+ msg->im6_msgtype = assert;
|
|
+ msg->im6_mbz = 0;
|
|
+ msg->im6_mif = vifi;
|
|
+ skb->h.raw = skb->nh.raw;
|
|
+ }
|
|
+
|
|
+ if (mroute6_socket == NULL) {
|
|
+ kfree_skb(skb);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Deliver to user space multicast routing algorithms
|
|
+ */
|
|
+ if ((ret=sock_queue_rcv_skb(mroute6_socket,skb))<0) {
|
|
+ if (net_ratelimit())
|
|
+ printk(KERN_WARNING "mroute6: pending queue full, dropping entries.\n");
|
|
+ kfree_skb(skb);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Queue a packet for resolution. It gets locked cache entry!
|
|
+ */
|
|
+
|
|
+static int
|
|
+ip6mr_cache_unresolved(vifi_t vifi, struct sk_buff *skb)
|
|
+{
|
|
+ int err;
|
|
+ struct mfc6_cache *c;
|
|
+
|
|
+ spin_lock_bh(&mfc_unres_lock);
|
|
+ for (c=mfc_unres_queue; c; c=c->next) {
|
|
+ if (IN6_ARE_ADDR_EQUAL(&c->mf6c_mcastgrp,&skb->nh.ipv6h->daddr) &&
|
|
+ IN6_ARE_ADDR_EQUAL(&c->mf6c_origin,&skb->nh.ipv6h->saddr))
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (c == NULL) {
|
|
+ /*
|
|
+ * Create a new entry if allowable
|
|
+ */
|
|
+
|
|
+ if (atomic_read(&cache_resolve_queue_len)>=10 ||
|
|
+ (c=ip6mr_cache_alloc_unres())==NULL) {
|
|
+ spin_unlock_bh(&mfc_unres_lock);
|
|
+
|
|
+ kfree_skb(skb);
|
|
+ return -ENOBUFS;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Fill in the new cache entry
|
|
+ */
|
|
+ c->mf6c_parent=-1;
|
|
+ c->mf6c_origin=skb->nh.ipv6h->saddr;
|
|
+ c->mf6c_mcastgrp=skb->nh.ipv6h->daddr;
|
|
+
|
|
+ /*
|
|
+ * Reflect first query at pim6sd
|
|
+ */
|
|
+ if ((err = ip6mr_cache_report(skb, vifi, MRT6MSG_NOCACHE))<0) {
|
|
+ /* If the report failed throw the cache entry
|
|
+ out - Brad Parker
|
|
+ */
|
|
+ spin_unlock_bh(&mfc_unres_lock);
|
|
+
|
|
+ kmem_cache_free(mrt_cachep, c);
|
|
+ kfree_skb(skb);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ atomic_inc(&cache_resolve_queue_len);
|
|
+ c->next = mfc_unres_queue;
|
|
+ mfc_unres_queue = c;
|
|
+
|
|
+ mod_timer(&ipmr_expire_timer, c->mfc_un.unres.expires);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * See if we can append the packet
|
|
+ */
|
|
+ if (c->mfc_un.unres.unresolved.qlen>3) {
|
|
+ kfree_skb(skb);
|
|
+ err = -ENOBUFS;
|
|
+ } else {
|
|
+ skb_queue_tail(&c->mfc_un.unres.unresolved,skb);
|
|
+ err = 0;
|
|
+ }
|
|
+
|
|
+ spin_unlock_bh(&mfc_unres_lock);
|
|
+ return err;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * MFC6 cache manipulation by user space
|
|
+ */
|
|
+
|
|
+static int ip6mr_mfc_delete(struct mf6cctl *mfc)
|
|
+{
|
|
+ int line;
|
|
+ struct mfc6_cache *c, **cp;
|
|
+
|
|
+ line=MFC6_HASH(mfc->mf6cc_mcastgrp.sin6_addr, mfc->mf6cc_origin.sin6_addr);
|
|
+
|
|
+ for (cp=&mfc6_cache_array[line]; (c=*cp) != NULL; cp = &c->next) {
|
|
+ if (IN6_ARE_ADDR_EQUAL(&c->mf6c_origin,&mfc->mf6cc_origin.sin6_addr) &&
|
|
+ IN6_ARE_ADDR_EQUAL(&c->mf6c_mcastgrp,&mfc->mf6cc_mcastgrp.sin6_addr)) {
|
|
+ write_lock_bh(&mrt_lock);
|
|
+ *cp = c->next;
|
|
+ write_unlock_bh(&mrt_lock);
|
|
+
|
|
+ kmem_cache_free(mrt_cachep, c);
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+ return -ENOENT;
|
|
+}
|
|
+
|
|
+static int ip6mr_device_event(struct notifier_block *this, unsigned long event, void *ptr)
|
|
+{
|
|
+ struct mif_device *v;
|
|
+ int ct;
|
|
+ if (event != NETDEV_UNREGISTER)
|
|
+ return NOTIFY_DONE;
|
|
+ v=&vif6_table[0];
|
|
+ for(ct=0;ct<maxvif;ct++,v++) {
|
|
+ if (v->dev==ptr)
|
|
+ mif6_delete(ct);
|
|
+ }
|
|
+ return NOTIFY_DONE;
|
|
+}
|
|
+
|
|
+static struct notifier_block ip6_mr_notifier = {
|
|
+ .notifier_call = ip6mr_device_event
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Setup for IP multicast routing
|
|
+ */
|
|
+
|
|
+void __init ip6_mr_init(void)
|
|
+{
|
|
+ printk(KERN_INFO "6WIND/LSIIT IPv6 multicast forwarding 0.1 plus PIM-SM/SSM with *BSD API\n");
|
|
+
|
|
+ mrt_cachep = kmem_cache_create("ip6_mrt_cache",
|
|
+ sizeof(struct mfc6_cache),
|
|
+ 0, SLAB_HWCACHE_ALIGN,
|
|
+ NULL, NULL);
|
|
+ if (!mrt_cachep)
|
|
+ panic("cannot allocate ip_mrt_cache");
|
|
+
|
|
+ init_timer(&ipmr_expire_timer);
|
|
+ ipmr_expire_timer.function=ipmr_expire_process;
|
|
+ register_netdevice_notifier(&ip6_mr_notifier);
|
|
+#ifdef CONFIG_PROC_FS
|
|
+ proc_net_fops_create("ip6_mr_vif", 0, &ip6mr_vif_fops);
|
|
+ proc_net_fops_create("ip6_mr_cache", 0, &ip6mr_mfc_fops);
|
|
+#endif
|
|
+}
|
|
+
|
|
+
|
|
+static int ip6mr_mfc_add(struct mf6cctl *mfc, int mrtsock)
|
|
+{
|
|
+ int line;
|
|
+ struct mfc6_cache *uc, *c, **cp;
|
|
+ unsigned char ttls[MAXVIFS];
|
|
+ int i;
|
|
+
|
|
+ memset(ttls, 255, MAXVIFS);
|
|
+ for(i=0;i<MAXVIFS;i++) {
|
|
+ if(IF_ISSET(i,&mfc->mf6cc_ifset))
|
|
+ ttls[i]=1;
|
|
+
|
|
+ }
|
|
+
|
|
+ line=MFC6_HASH(mfc->mf6cc_mcastgrp.sin6_addr, mfc->mf6cc_origin.sin6_addr);
|
|
+
|
|
+ for (cp=&mfc6_cache_array[line]; (c=*cp) != NULL; cp = &c->next) {
|
|
+ if (IN6_ARE_ADDR_EQUAL(&c->mf6c_origin,&mfc->mf6cc_origin.sin6_addr) &&
|
|
+ IN6_ARE_ADDR_EQUAL(&c->mf6c_mcastgrp,&mfc->mf6cc_mcastgrp.sin6_addr))
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (c != NULL) {
|
|
+ write_lock_bh(&mrt_lock);
|
|
+ c->mf6c_parent = mfc->mf6cc_parent;
|
|
+ ip6mr_update_threshoulds(c, ttls);
|
|
+ if (!mrtsock)
|
|
+ c->mfc_flags |= MFC_STATIC;
|
|
+ write_unlock_bh(&mrt_lock);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if(!(ipv6_addr_type(&mfc->mf6cc_mcastgrp.sin6_addr)&IPV6_ADDR_MULTICAST))
|
|
+ return -EINVAL;
|
|
+
|
|
+ c=ip6mr_cache_alloc();
|
|
+ if (c==NULL)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ c->mf6c_origin=mfc->mf6cc_origin.sin6_addr;
|
|
+ c->mf6c_mcastgrp=mfc->mf6cc_mcastgrp.sin6_addr;
|
|
+ c->mf6c_parent=mfc->mf6cc_parent;
|
|
+ ip6mr_update_threshoulds(c, ttls);
|
|
+ if (!mrtsock)
|
|
+ c->mfc_flags |= MFC_STATIC;
|
|
+
|
|
+ write_lock_bh(&mrt_lock);
|
|
+ c->next = mfc6_cache_array[line];
|
|
+ mfc6_cache_array[line] = c;
|
|
+ write_unlock_bh(&mrt_lock);
|
|
+
|
|
+ /*
|
|
+ * Check to see if we resolved a queued list. If so we
|
|
+ * need to send on the frames and tidy up.
|
|
+ */
|
|
+ spin_lock_bh(&mfc_unres_lock);
|
|
+ for (cp = &mfc_unres_queue; (uc=*cp) != NULL;
|
|
+ cp = &uc->next) {
|
|
+ if (IN6_ARE_ADDR_EQUAL(&uc->mf6c_origin,&c->mf6c_origin) &&
|
|
+ IN6_ARE_ADDR_EQUAL(&uc->mf6c_mcastgrp,&c->mf6c_mcastgrp)) {
|
|
+ *cp = uc->next;
|
|
+ if (atomic_dec_and_test(&cache_resolve_queue_len))
|
|
+ del_timer(&ipmr_expire_timer);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ spin_unlock_bh(&mfc_unres_lock);
|
|
+
|
|
+ if (uc) {
|
|
+ ip6mr_cache_resolve(uc, c);
|
|
+ kmem_cache_free(mrt_cachep, uc);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Close the multicast socket, and clear the vif tables etc
|
|
+ */
|
|
+
|
|
+static void mroute_clean_tables(struct sock *sk)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ /*
|
|
+ * Shut down all active vif entries
|
|
+ */
|
|
+ for(i=0; i<maxvif; i++) {
|
|
+ if (!(vif6_table[i].flags&VIFF_STATIC))
|
|
+ mif6_delete(i);
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * Wipe the cache
|
|
+ */
|
|
+ for (i=0;i<MFC_LINES;i++) {
|
|
+ struct mfc6_cache *c, **cp;
|
|
+
|
|
+ cp = &mfc6_cache_array[i];
|
|
+ while ((c = *cp) != NULL) {
|
|
+ if (c->mfc_flags&MFC_STATIC) {
|
|
+ cp = &c->next;
|
|
+ continue;
|
|
+ }
|
|
+ write_lock_bh(&mrt_lock);
|
|
+ *cp = c->next;
|
|
+ write_unlock_bh(&mrt_lock);
|
|
+
|
|
+ kmem_cache_free(mrt_cachep, c);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (atomic_read(&cache_resolve_queue_len) != 0) {
|
|
+ struct mfc6_cache *c;
|
|
+
|
|
+ spin_lock_bh(&mfc_unres_lock);
|
|
+ while (mfc_unres_queue != NULL) {
|
|
+ c = mfc_unres_queue;
|
|
+ mfc_unres_queue = c->next;
|
|
+ spin_unlock_bh(&mfc_unres_lock);
|
|
+
|
|
+ ip6mr_destroy_unres(c);
|
|
+
|
|
+ spin_lock_bh(&mfc_unres_lock);
|
|
+ }
|
|
+ spin_unlock_bh(&mfc_unres_lock);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void mrtsock_destruct(struct sock *sk)
|
|
+{
|
|
+ rtnl_lock();
|
|
+ if (sk == mroute6_socket) {
|
|
+ ipv6_devconf.mc_forwarding--;
|
|
+
|
|
+ write_lock_bh(&mrt_lock);
|
|
+ mroute6_socket=NULL;
|
|
+ write_unlock_bh(&mrt_lock);
|
|
+
|
|
+ mroute_clean_tables(sk);
|
|
+ }
|
|
+ rtnl_unlock();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Socket options and virtual interface manipulation. The whole
|
|
+ * virtual interface system is a complete heap, but unfortunately
|
|
+ * that's how BSD mrouted happens to think. Maybe one day with a proper
|
|
+ * MOSPF/PIM router set up we can clean this up.
|
|
+ */
|
|
+
|
|
+int ip6_mroute_setsockopt(struct sock *sk,int optname,char __user *optval,int optlen)
|
|
+{
|
|
+ int ret;
|
|
+ struct mif6ctl vif;
|
|
+ struct mf6cctl mfc;
|
|
+ mifi_t mifi;
|
|
+
|
|
+ if(optname!=MRT6_INIT)
|
|
+ {
|
|
+ if(sk!=mroute6_socket && !capable(CAP_NET_ADMIN))
|
|
+ return -EACCES;
|
|
+ }
|
|
+
|
|
+ switch(optname)
|
|
+ {
|
|
+ case MRT6_INIT:
|
|
+ if (sk->sk_type != SOCK_RAW ||
|
|
+ inet_sk(sk)->num != IPPROTO_ICMPV6)
|
|
+ return -EOPNOTSUPP;
|
|
+ if(optlen!=sizeof(int))
|
|
+ return -ENOPROTOOPT;
|
|
+
|
|
+ rtnl_lock();
|
|
+ if (mroute6_socket) {
|
|
+ rtnl_unlock();
|
|
+ return -EADDRINUSE;
|
|
+ }
|
|
+
|
|
+ ret = ip6_ra_control(sk, 1, mrtsock_destruct);
|
|
+ if (ret == 0) {
|
|
+ write_lock_bh(&mrt_lock);
|
|
+ mroute6_socket=sk;
|
|
+ write_unlock_bh(&mrt_lock);
|
|
+
|
|
+ ipv6_devconf.mc_forwarding++;
|
|
+ }
|
|
+ rtnl_unlock();
|
|
+ return ret;
|
|
+ case MRT6_DONE:
|
|
+ if (sk!=mroute6_socket)
|
|
+ return -EACCES;
|
|
+ return ip6_ra_control(sk, -1, NULL);
|
|
+ case MRT6_ADD_MIF:
|
|
+ if(optlen!=sizeof(vif))
|
|
+ return -EINVAL;
|
|
+ if (copy_from_user(&vif,optval,sizeof(vif)))
|
|
+ return -EFAULT;
|
|
+ if(vif.mif6c_mifi >= MAXVIFS)
|
|
+ return -ENFILE;
|
|
+ rtnl_lock();
|
|
+ ret = mif6_add(&vif, sk==mroute6_socket);
|
|
+ rtnl_unlock();
|
|
+ return ret;
|
|
+ case MRT6_DEL_MIF:
|
|
+ if(optlen!=sizeof(mifi_t))
|
|
+ return -EINVAL;
|
|
+ if (copy_from_user(&mifi,optval,sizeof(mifi_t)))
|
|
+ return -EFAULT;
|
|
+ rtnl_lock();
|
|
+ ret = mif6_delete(mifi);
|
|
+ rtnl_unlock();
|
|
+ return ret;
|
|
+
|
|
+ /*
|
|
+ * Manipulate the forwarding caches. These live
|
|
+ * in a sort of kernel/user symbiosis.
|
|
+ */
|
|
+ case MRT6_ADD_MFC:
|
|
+ case MRT6_DEL_MFC:
|
|
+ if(optlen!=sizeof(mfc))
|
|
+ return -EINVAL;
|
|
+ if (copy_from_user(&mfc,optval, sizeof(mfc)))
|
|
+ return -EFAULT;
|
|
+ rtnl_lock();
|
|
+ if (optname==MRT6_DEL_MFC)
|
|
+ ret = ip6mr_mfc_delete(&mfc);
|
|
+ else
|
|
+ ret = ip6mr_mfc_add(&mfc, sk==mroute6_socket);
|
|
+ rtnl_unlock();
|
|
+ return ret;
|
|
+ /*
|
|
+ * Control PIM assert (to activate pim will activate assert)
|
|
+ */
|
|
+ case MRT6_ASSERT:
|
|
+ {
|
|
+ int v;
|
|
+ if(get_user(v,(int __user *)optval))
|
|
+ return -EFAULT;
|
|
+ mroute_do_assert=(v)?1:0;
|
|
+ return 0;
|
|
+ }
|
|
+#ifdef CONFIG_IPV6_PIMSM_V2
|
|
+ case MRT6_PIM:
|
|
+ {
|
|
+ int v, ret;
|
|
+ if(get_user(v,(int __user *)optval))
|
|
+ return -EFAULT;
|
|
+ v = (v)?1:0;
|
|
+ rtnl_lock();
|
|
+ ret = 0;
|
|
+ if (v != mroute_do_pim) {
|
|
+ mroute_do_pim = v;
|
|
+ mroute_do_assert = v;
|
|
+ if (mroute_do_pim)
|
|
+ ret = inet6_add_protocol(&pim6_protocol,
|
|
+ IPPROTO_PIM);
|
|
+ else
|
|
+ ret = inet6_del_protocol(&pim6_protocol,
|
|
+ IPPROTO_PIM);
|
|
+ if (ret < 0)
|
|
+ ret = -EAGAIN;
|
|
+ }
|
|
+ rtnl_unlock();
|
|
+ return ret;
|
|
+ }
|
|
+#endif
|
|
+ /*
|
|
+ * Spurious command, or MRT_VERSION which you cannot
|
|
+ * set.
|
|
+ */
|
|
+ default:
|
|
+ return -ENOPROTOOPT;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Getsock opt support for the multicast routing system.
|
|
+ */
|
|
+
|
|
+int ip6_mroute_getsockopt(struct sock *sk,int optname,char __user *optval,int __user *optlen)
|
|
+{
|
|
+ int olr;
|
|
+ int val;
|
|
+
|
|
+ if(optname!=MRT6_VERSION &&
|
|
+#ifdef CONFIG_IPV6_PIMSM_V2
|
|
+ optname!=MRT6_PIM &&
|
|
+#endif
|
|
+ optname!=MRT6_ASSERT)
|
|
+ return -ENOPROTOOPT;
|
|
+
|
|
+ if (get_user(olr, optlen))
|
|
+ return -EFAULT;
|
|
+
|
|
+ olr = min_t(unsigned int, olr, sizeof(int));
|
|
+ if (olr < 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if(put_user(olr,optlen))
|
|
+ return -EFAULT;
|
|
+ if(optname==MRT6_VERSION)
|
|
+ val=0x0305;
|
|
+#ifdef CONFIG_IPV6_PIMSM_V2
|
|
+ else if(optname==MRT6_PIM)
|
|
+ val=mroute_do_pim;
|
|
+#endif
|
|
+ else
|
|
+ val=mroute_do_assert;
|
|
+ if(copy_to_user(optval,&val,olr))
|
|
+ return -EFAULT;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * The IP multicast ioctl support routines.
|
|
+ */
|
|
+
|
|
+int ip6mr_ioctl(struct sock *sk, int cmd, void __user *arg)
|
|
+{
|
|
+ struct sioc_sg_req6 sr;
|
|
+ struct sioc_mif_req6 vr;
|
|
+ struct mif_device *vif;
|
|
+ struct mfc6_cache *c;
|
|
+
|
|
+ switch(cmd)
|
|
+ {
|
|
+ case SIOCGETMIFCNT_IN6:
|
|
+ if (copy_from_user(&vr,arg,sizeof(vr)))
|
|
+ return -EFAULT;
|
|
+ if(vr.mifi>=maxvif)
|
|
+ return -EINVAL;
|
|
+ read_lock(&mrt_lock);
|
|
+ vif=&vif6_table[vr.mifi];
|
|
+ if(MIF_EXISTS(vr.mifi)) {
|
|
+ vr.icount=vif->pkt_in;
|
|
+ vr.ocount=vif->pkt_out;
|
|
+ vr.ibytes=vif->bytes_in;
|
|
+ vr.obytes=vif->bytes_out;
|
|
+ read_unlock(&mrt_lock);
|
|
+
|
|
+ if (copy_to_user(arg,&vr,sizeof(vr)))
|
|
+ return -EFAULT;
|
|
+ return 0;
|
|
+ }
|
|
+ read_unlock(&mrt_lock);
|
|
+ return -EADDRNOTAVAIL;
|
|
+ case SIOCGETSGCNT_IN6:
|
|
+ if (copy_from_user(&sr,arg,sizeof(sr)))
|
|
+ return -EFAULT;
|
|
+
|
|
+ read_lock(&mrt_lock);
|
|
+ c = ip6mr_cache_find(sr.src.sin6_addr, sr.grp.sin6_addr);
|
|
+ if (c) {
|
|
+ sr.pktcnt = c->mfc_un.res.pkt;
|
|
+ sr.bytecnt = c->mfc_un.res.bytes;
|
|
+ sr.wrong_if = c->mfc_un.res.wrong_if;
|
|
+ read_unlock(&mrt_lock);
|
|
+
|
|
+ if (copy_to_user(arg,&sr,sizeof(sr)))
|
|
+ return -EFAULT;
|
|
+ return 0;
|
|
+ }
|
|
+ read_unlock(&mrt_lock);
|
|
+ return -EADDRNOTAVAIL;
|
|
+ default:
|
|
+ return -ENOIOCTLCMD;
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
+static inline int ip6mr_forward_finish(struct sk_buff *skb)
|
|
+{
|
|
+#ifdef notyet
|
|
+ struct ip_options * opt = &(IP6CB(skb)->opt);
|
|
+
|
|
+ IP_INC_STATS_BH(OutForwDatagrams);
|
|
+
|
|
+ if (unlikely(opt->optlen))
|
|
+ ip_forward_options(skb);
|
|
+#endif
|
|
+
|
|
+ return dst_output(skb);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Processing handlers for ip6mr_forward
|
|
+ */
|
|
+
|
|
+static void ip6mr_queue_xmit(struct sk_buff *skb, struct mfc6_cache *c, int vifi)
|
|
+{
|
|
+ struct ipv6hdr *ipv6h = skb->nh.ipv6h;
|
|
+ struct mif_device *vif = &vif6_table[vifi];
|
|
+ struct net_device *dev;
|
|
+#if 0
|
|
+ struct rtable *rt;
|
|
+ int encap = 0;
|
|
+#endif
|
|
+ struct in6_addr *snd_addr=&ipv6h->daddr;
|
|
+ int full_len = skb->len;
|
|
+
|
|
+ if (vif->dev == NULL)
|
|
+ goto out_free;
|
|
+
|
|
+#ifdef CONFIG_IPV6_PIMSM_V2
|
|
+ if (vif->flags & MIFF_REGISTER) {
|
|
+ vif->pkt_out++;
|
|
+ vif->bytes_out+=skb->len;
|
|
+ ((struct net_device_stats*)vif->dev->priv)->tx_bytes += skb->len;
|
|
+ ((struct net_device_stats*)vif->dev->priv)->tx_packets++;
|
|
+ ip6mr_cache_report(skb, vifi, MRT6MSG_WHOLEPKT);
|
|
+ kfree_skb(skb);
|
|
+ return;
|
|
+ }
|
|
+#endif
|
|
+ /*
|
|
+ * RFC1584 teaches, that DVMRP/PIM router must deliver packets locally
|
|
+ * not only before forwarding, but after forwarding on all output
|
|
+ * interfaces. It is clear, if mrouter runs a multicasting
|
|
+ * program, it should receive packets not depending to what interface
|
|
+ * program is joined.
|
|
+ * If we will not make it, the program will have to join on all
|
|
+ * interfaces. On the other hand, multihoming host (or router, but
|
|
+ * not mrouter) cannot join to more than one interface - it will
|
|
+ * result in receiving multiple packets.
|
|
+ */
|
|
+ dev = vif->dev;
|
|
+ skb->dev=dev;
|
|
+ vif->pkt_out++;
|
|
+ vif->bytes_out+=skb->len;
|
|
+
|
|
+ ipv6h = skb->nh.ipv6h;
|
|
+
|
|
+ ipv6h->hop_limit--;
|
|
+
|
|
+ if(dev->hard_header) {
|
|
+ unsigned char ha[MAX_ADDR_LEN];
|
|
+ ndisc_mc_map(snd_addr,ha,dev,1);
|
|
+ if(dev->hard_header(skb,dev, ETH_P_IPV6,ha,NULL,full_len) < 0)
|
|
+ goto out_free;
|
|
+ }
|
|
+
|
|
+ NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, skb->dev, dev,
|
|
+ dev_queue_xmit);
|
|
+/* NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, skb, skb->dev, dev,
|
|
+ ip6mr_forward_finish);
|
|
+*/
|
|
+
|
|
+
|
|
+ /* NF_HOOK(PF_INET6, NF_IP6_FORWARD, skb, skb->dev, dev,
|
|
+ ip6mr_forward_finish);
|
|
+ */
|
|
+ return;
|
|
+ /* XXX */
|
|
+
|
|
+out_free:
|
|
+ kfree_skb(skb);
|
|
+ return;
|
|
+}
|
|
+
|
|
+static int ip6mr_find_vif(struct net_device *dev)
|
|
+{
|
|
+ int ct;
|
|
+ for (ct=maxvif-1; ct>=0; ct--) {
|
|
+ if (vif6_table[ct].dev == dev)
|
|
+ break;
|
|
+ }
|
|
+ return ct;
|
|
+}
|
|
+
|
|
+static int ip6_mr_forward(struct sk_buff *skb, struct mfc6_cache *cache, int local)
|
|
+{
|
|
+ int psend = -1;
|
|
+ int vif, ct;
|
|
+
|
|
+ vif = cache->mf6c_parent;
|
|
+ cache->mfc_un.res.pkt++;
|
|
+ cache->mfc_un.res.bytes += skb->len;
|
|
+
|
|
+ /*
|
|
+ * Wrong interface: drop packet and (maybe) send PIM assert.
|
|
+ */
|
|
+ if (vif6_table[vif].dev != skb->dev) {
|
|
+ int true_vifi;
|
|
+
|
|
+ if (((struct rtable*)skb->dst)->fl.iif == 0) {
|
|
+ /* It is our own packet, looped back.
|
|
+ Very complicated situation...
|
|
+
|
|
+ The best workaround until routing daemons will be
|
|
+ fixed is not to redistribute packet, if it was
|
|
+ send through wrong interface. It means, that
|
|
+ multicast applications WILL NOT work for
|
|
+ (S,G), which have default multicast route pointing
|
|
+ to wrong oif. In any case, it is not a good
|
|
+ idea to use multicasting applications on router.
|
|
+ */
|
|
+ goto dont_forward;
|
|
+ }
|
|
+
|
|
+ cache->mfc_un.res.wrong_if++;
|
|
+ true_vifi = ip6mr_find_vif(skb->dev);
|
|
+
|
|
+ if (true_vifi >= 0 && mroute_do_assert &&
|
|
+ /* pimsm uses asserts, when switching from RPT to SPT,
|
|
+ so that we cannot check that packet arrived on an oif.
|
|
+ It is bad, but otherwise we would need to move pretty
|
|
+ large chunk of pimd to kernel. Ough... --ANK
|
|
+ */
|
|
+ (mroute_do_pim || cache->mfc_un.res.ttls[true_vifi] < 255) &&
|
|
+ time_after(jiffies,
|
|
+ cache->mfc_un.res.last_assert + MFC_ASSERT_THRESH)) {
|
|
+ cache->mfc_un.res.last_assert = jiffies;
|
|
+ ip6mr_cache_report(skb, true_vifi, MRT6MSG_WRONGMIF);
|
|
+ }
|
|
+ goto dont_forward;
|
|
+ }
|
|
+
|
|
+ vif6_table[vif].pkt_in++;
|
|
+ vif6_table[vif].bytes_in+=skb->len;
|
|
+
|
|
+ /*
|
|
+ * Forward the frame
|
|
+ */
|
|
+ for (ct = cache->mfc_un.res.maxvif-1; ct >= cache->mfc_un.res.minvif; ct--) {
|
|
+ if (skb->nh.ipv6h->hop_limit > cache->mfc_un.res.ttls[ct]) {
|
|
+ struct ipv6hdr *ipv6h = skb->nh.ipv6h;
|
|
+ if (psend != -1) {
|
|
+ struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
|
|
+ if (skb2) {
|
|
+ ip6mr_queue_xmit(skb2, cache, psend);
|
|
+ ipv6h->hop_limit++;
|
|
+ }
|
|
+ }
|
|
+ psend=ct;
|
|
+ }
|
|
+ }
|
|
+ if (psend != -1) {
|
|
+ struct ipv6hdr *ipv6h = skb->nh.ipv6h;
|
|
+ if (local) {
|
|
+ struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
|
|
+ if (skb2) {
|
|
+ ip6mr_queue_xmit(skb2, cache, psend);
|
|
+ ipv6h->hop_limit++;
|
|
+ }
|
|
+ } else {
|
|
+ ip6mr_queue_xmit(skb, cache, psend);
|
|
+ ipv6h->hop_limit++;
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+dont_forward:
|
|
+ if (!local)
|
|
+ kfree_skb(skb);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+/*
|
|
+ * Multicast packets for forwarding arrive here
|
|
+ */
|
|
+
|
|
+int ip6_mr_input(struct sk_buff *skb)
|
|
+{
|
|
+ struct mfc6_cache *cache;
|
|
+ int local = ((struct rt6_info*)skb->dst)->rt6i_flags&RTCF_LOCAL;
|
|
+#if 0
|
|
+ IP6CB(skb)->flags = 0;
|
|
+#endif
|
|
+
|
|
+ read_lock(&mrt_lock);
|
|
+ cache = ip6mr_cache_find(skb->nh.ipv6h->saddr, skb->nh.ipv6h->daddr);
|
|
+
|
|
+ /*
|
|
+ * No usable cache entry
|
|
+ */
|
|
+ if (cache==NULL) {
|
|
+ int vif;
|
|
+
|
|
+ vif = ip6mr_find_vif(skb->dev);
|
|
+ if (vif >= 0) {
|
|
+ int err = ip6mr_cache_unresolved(vif, skb);
|
|
+ read_unlock(&mrt_lock);
|
|
+
|
|
+ return err;
|
|
+ }
|
|
+ read_unlock(&mrt_lock);
|
|
+ kfree_skb(skb);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ ip6_mr_forward(skb, cache, local);
|
|
+
|
|
+ read_unlock(&mrt_lock);
|
|
+
|
|
+ return 0;
|
|
+#if 0
|
|
+dont_forward:
|
|
+ kfree_skb(skb);
|
|
+ return 0;
|
|
+#endif
|
|
+}
|
|
+
|
|
+
|
|
+static int
|
|
+ip6mr_fill_mroute(struct sk_buff *skb, struct mfc6_cache *c, struct rtmsg *rtm)
|
|
+{
|
|
+ int ct;
|
|
+ struct rtnexthop *nhp;
|
|
+ struct net_device *dev = vif6_table[c->mf6c_parent].dev;
|
|
+ u8 *b = skb->tail;
|
|
+ struct rtattr *mp_head;
|
|
+
|
|
+ if (dev)
|
|
+ RTA_PUT(skb, RTA_IIF, 4, &dev->ifindex);
|
|
+
|
|
+ mp_head = (struct rtattr*)skb_put(skb, RTA_LENGTH(0));
|
|
+
|
|
+ for (ct = c->mfc_un.res.minvif; ct < c->mfc_un.res.maxvif; ct++) {
|
|
+ if (c->mfc_un.res.ttls[ct] < 255) {
|
|
+ if (skb_tailroom(skb) < RTA_ALIGN(RTA_ALIGN(sizeof(*nhp)) + 4))
|
|
+ goto rtattr_failure;
|
|
+ nhp = (struct rtnexthop*)skb_put(skb, RTA_ALIGN(sizeof(*nhp)));
|
|
+ nhp->rtnh_flags = 0;
|
|
+ nhp->rtnh_hops = c->mfc_un.res.ttls[ct];
|
|
+ nhp->rtnh_ifindex = vif6_table[ct].dev->ifindex;
|
|
+ nhp->rtnh_len = sizeof(*nhp);
|
|
+ }
|
|
+ }
|
|
+ mp_head->rta_type = RTA_MULTIPATH;
|
|
+ mp_head->rta_len = skb->tail - (u8*)mp_head;
|
|
+ rtm->rtm_type = RTN_MULTICAST;
|
|
+ return 1;
|
|
+
|
|
+rtattr_failure:
|
|
+ skb_trim(skb, b - skb->data);
|
|
+ return -EMSGSIZE;
|
|
+}
|
|
diff -urN linux-2.6.11/net/ipv6/ipv6_sockglue.c x1/net/ipv6/ipv6_sockglue.c
|
|
--- linux-2.6.11/net/ipv6/ipv6_sockglue.c 2005-03-02 08:37:48.000000000 +0100
|
|
+++ x1/net/ipv6/ipv6_sockglue.c 2005-02-09 16:31:39.000000000 +0100
|
|
@@ -23,6 +23,8 @@
|
|
* Changes:
|
|
* David L Stevens <dlstevens@us.ibm.com>:
|
|
* - added multicast source filtering API for MLDv2
|
|
+ * Hoerdt Mickael <hoerdt@clarinet.u-strasbg.fr>:
|
|
+ * - added multicat routing support for IPv6
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
@@ -55,6 +57,10 @@
|
|
|
|
#include <asm/uaccess.h>
|
|
|
|
+#ifdef CONFIG_IPV6_MROUTE
|
|
+#include <linux/mroute6.h>
|
|
+#endif
|
|
+
|
|
DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics);
|
|
|
|
static struct packet_type ipv6_packet_type = {
|
|
@@ -69,9 +75,14 @@
|
|
{
|
|
struct ip6_ra_chain *ra, *new_ra, **rap;
|
|
|
|
+#ifndef CONFIG_IPV6_MROUTE
|
|
/* RA packet may be delivered ONLY to IPPROTO_RAW socket */
|
|
if (sk->sk_type != SOCK_RAW || inet_sk(sk)->num != IPPROTO_RAW)
|
|
return -EINVAL;
|
|
+#else
|
|
+ if (sk->sk_type != SOCK_RAW || inet_sk(sk)->num == IPPROTO_RAW)
|
|
+ return -EINVAL;
|
|
+#endif
|
|
|
|
new_ra = (sel>=0) ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
|
|
|
|
@@ -136,6 +147,11 @@
|
|
|
|
valbool = (val!=0);
|
|
|
|
+#ifdef CONFIG_IPV6_MROUTE
|
|
+ if(optname >= MRT6_BASE && optname <= (MRT6_BASE + 10))
|
|
+ return ip6_mroute_setsockopt(sk,optname,optval,optlen);
|
|
+#endif
|
|
+
|
|
lock_sock(sk);
|
|
|
|
switch (optname) {
|
|
@@ -535,6 +551,11 @@
|
|
return udp_prot.getsockopt(sk, level, optname, optval, optlen);
|
|
if(level!=SOL_IPV6)
|
|
return -ENOPROTOOPT;
|
|
+
|
|
+#ifdef CONFIG_IPV6_MROUTE
|
|
+ if(optname >= MRT6_BASE && optname <= (MRT6_BASE + 10))
|
|
+ return ip6_mroute_getsockopt(sk,optname,optval,optlen);
|
|
+#endif
|
|
if (get_user(len, optlen))
|
|
return -EFAULT;
|
|
switch (optname) {
|
|
diff -urN linux-2.6.11/net/ipv6/ipv6_syms.c x1/net/ipv6/ipv6_syms.c
|
|
--- linux-2.6.11/net/ipv6/ipv6_syms.c 2005-03-02 08:38:13.000000000 +0100
|
|
+++ x1/net/ipv6/ipv6_syms.c 2005-02-03 05:44:15.000000000 +0100
|
|
@@ -7,7 +7,7 @@
|
|
#include <net/ip6_route.h>
|
|
#include <net/xfrm.h>
|
|
|
|
-EXPORT_SYMBOL(ipv6_addr_type);
|
|
+EXPORT_SYMBOL(__ipv6_addr_type);
|
|
EXPORT_SYMBOL(icmpv6_send);
|
|
EXPORT_SYMBOL(icmpv6_statistics);
|
|
EXPORT_SYMBOL(icmpv6_err_convert);
|
|
@@ -38,4 +38,8 @@
|
|
#endif
|
|
EXPORT_SYMBOL(rt6_lookup);
|
|
EXPORT_SYMBOL(fl6_sock_lookup);
|
|
+EXPORT_SYMBOL(ip6_append_data);
|
|
+EXPORT_SYMBOL(ip6_flush_pending_frames);
|
|
+EXPORT_SYMBOL(ip6_push_pending_frames);
|
|
+EXPORT_SYMBOL(ip6_dst_lookup);
|
|
EXPORT_SYMBOL(ipv6_push_nfrag_opts);
|
|
diff -urN linux-2.6.11/net/ipv6/mcast.c x1/net/ipv6/mcast.c
|
|
--- linux-2.6.11/net/ipv6/mcast.c 2005-03-02 08:38:25.000000000 +0100
|
|
+++ x1/net/ipv6/mcast.c 2005-02-09 16:31:39.000000000 +0100
|
|
@@ -62,6 +62,11 @@
|
|
|
|
#include <net/ip6_checksum.h>
|
|
|
|
+#ifdef CONFIG_IPV6_MROUTE
|
|
+#include <linux/mroute6.h>
|
|
+int ip6_dev_loopback_xmit(struct sk_buff *newskb);
|
|
+#endif
|
|
+
|
|
/* Set to 3 to get tracing... */
|
|
#define MCAST_DEBUG 2
|
|
|
|
@@ -1320,7 +1325,11 @@
|
|
struct inet6_dev *idev = in6_dev_get(skb->dev);
|
|
int err;
|
|
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS);
|
|
+#endif
|
|
payload_len = skb->tail - (unsigned char *)skb->nh.ipv6h -
|
|
sizeof(struct ipv6hdr);
|
|
mldlen = skb->tail - skb->h.raw;
|
|
@@ -1330,11 +1339,33 @@
|
|
IPPROTO_ICMPV6, csum_partial(skb->h.raw, mldlen, 0));
|
|
err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dev,
|
|
dev_queue_xmit);
|
|
+
|
|
+#ifdef CONFIG_IPV6_MROUTE
|
|
+ /*
|
|
+ * if we are acting as a multicast router, loopback a copy to the
|
|
+ * process level multicast routing daemon
|
|
+ */
|
|
+ if (mroute6_socket != NULL) {
|
|
+ struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
|
|
+ if (newskb)
|
|
+ NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, newskb, NULL,
|
|
+ newskb->dev, ip6_dev_loopback_xmit);
|
|
+ }
|
|
+#endif
|
|
+
|
|
if (!err) {
|
|
ICMP6_INC_STATS(idev,ICMP6_MIB_OUTMSGS);
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTMCASTPKTS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTMCASTPKTS);
|
|
+#endif
|
|
} else
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS);
|
|
+#endif
|
|
|
|
if (likely(idev != NULL))
|
|
in6_dev_put(idev);
|
|
@@ -1604,7 +1635,11 @@
|
|
static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type)
|
|
{
|
|
struct sock *sk = igmp6_socket->sk;
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct inet6_dev *idev = __in6_dev_get(dev);
|
|
+#else
|
|
struct inet6_dev *idev;
|
|
+#endif
|
|
struct sk_buff *skb;
|
|
struct icmp6hdr *hdr;
|
|
struct in6_addr *snd_addr;
|
|
@@ -1616,7 +1651,11 @@
|
|
IPV6_TLV_ROUTERALERT, 2, 0, 0,
|
|
IPV6_TLV_PADN, 0 };
|
|
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS);
|
|
+#endif
|
|
snd_addr = addr;
|
|
if (type == ICMPV6_MGM_REDUCTION) {
|
|
snd_addr = &all_routers;
|
|
@@ -1630,7 +1669,11 @@
|
|
skb = sock_alloc_send_skb(sk, LL_RESERVED_SPACE(dev) + full_len, 1, &err);
|
|
|
|
if (skb == NULL) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS);
|
|
+#endif
|
|
return;
|
|
}
|
|
|
|
@@ -1669,22 +1712,47 @@
|
|
|
|
err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, skb->dev,
|
|
dev_queue_xmit);
|
|
+
|
|
+#ifdef CONFIG_IPV6_MROUTE
|
|
+ /*
|
|
+ * if we are acting as a multicast router, loopback a copy to the
|
|
+ * process level multicast routing daemon
|
|
+ */
|
|
+ if (mroute6_socket != NULL) {
|
|
+ struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
|
|
+ if (newskb)
|
|
+ NF_HOOK(PF_INET6, NF_IP6_POST_ROUTING, newskb, NULL,
|
|
+ newskb->dev, ip6_dev_loopback_xmit);
|
|
+ }
|
|
+#endif
|
|
if (!err) {
|
|
if (type == ICMPV6_MGM_REDUCTION)
|
|
ICMP6_INC_STATS(idev, ICMP6_MIB_OUTGROUPMEMBREDUCTIONS);
|
|
else
|
|
ICMP6_INC_STATS(idev, ICMP6_MIB_OUTGROUPMEMBRESPONSES);
|
|
ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTMCASTPKTS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTMCASTPKTS);
|
|
+#endif
|
|
} else
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS);
|
|
+#endif
|
|
|
|
if (likely(idev != NULL))
|
|
in6_dev_put(idev);
|
|
return;
|
|
|
|
out:
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS);
|
|
+#endif
|
|
kfree_skb(skb);
|
|
}
|
|
|
|
diff -urN linux-2.6.11/net/ipv6/ndisc.c x1/net/ipv6/ndisc.c
|
|
--- linux-2.6.11/net/ipv6/ndisc.c 2005-03-02 08:38:10.000000000 +0100
|
|
+++ x1/net/ipv6/ndisc.c 2005-02-16 10:16:52.000000000 +0100
|
|
@@ -437,6 +437,7 @@
|
|
return;
|
|
}
|
|
|
|
+ ND_PRINTK2("%s:dst=%p\n", __FUNCTION__, dst);
|
|
if (inc_opt) {
|
|
if (dev->addr_len)
|
|
len += NDISC_OPT_SPACE(dev->addr_len);
|
|
@@ -444,7 +445,7 @@
|
|
inc_opt = 0;
|
|
}
|
|
|
|
- skb = sock_alloc_send_skb(sk, MAX_HEADER + len + LL_RESERVED_SPACE(dev),
|
|
+ skb = sock_alloc_send_skb(sk, MAX_HEADER + len + LL_RESERVED_SPACE(dev) + dst->header_len + 64,
|
|
1, &err);
|
|
|
|
if (skb == NULL) {
|
|
@@ -485,7 +486,11 @@
|
|
|
|
skb->dst = dst;
|
|
idev = in6_dev_get(dst->dev);
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS);
|
|
+#endif
|
|
err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output);
|
|
if (!err) {
|
|
ICMP6_INC_STATS(idev, ICMP6_MIB_OUTNEIGHBORADVERTISEMENTS);
|
|
@@ -534,7 +539,7 @@
|
|
if (send_llinfo)
|
|
len += NDISC_OPT_SPACE(dev->addr_len);
|
|
|
|
- skb = sock_alloc_send_skb(sk, MAX_HEADER + len + LL_RESERVED_SPACE(dev),
|
|
+ skb = sock_alloc_send_skb(sk, MAX_HEADER + len + LL_RESERVED_SPACE(dev) + dst->header_len + 64,
|
|
1, &err);
|
|
if (skb == NULL) {
|
|
ND_PRINTK0(KERN_ERR
|
|
@@ -570,7 +575,11 @@
|
|
/* send it! */
|
|
skb->dst = dst;
|
|
idev = in6_dev_get(dst->dev);
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS);
|
|
+#endif
|
|
err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output);
|
|
if (!err) {
|
|
ICMP6_INC_STATS(idev, ICMP6_MIB_OUTNEIGHBORSOLICITS);
|
|
@@ -610,7 +619,7 @@
|
|
if (dev->addr_len)
|
|
len += NDISC_OPT_SPACE(dev->addr_len);
|
|
|
|
- skb = sock_alloc_send_skb(sk, MAX_HEADER + len + LL_RESERVED_SPACE(dev),
|
|
+ skb = sock_alloc_send_skb(sk, MAX_HEADER + len + LL_RESERVED_SPACE(dev) + dst->header_len + 64,
|
|
1, &err);
|
|
if (skb == NULL) {
|
|
ND_PRINTK0(KERN_ERR
|
|
@@ -644,7 +653,11 @@
|
|
/* send it! */
|
|
skb->dst = dst;
|
|
idev = in6_dev_get(dst->dev);
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS);
|
|
+#endif
|
|
err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output);
|
|
if (!err) {
|
|
ICMP6_INC_STATS(idev, ICMP6_MIB_OUTROUTERSOLICITS);
|
|
@@ -809,7 +822,6 @@
|
|
* sender should delay its response
|
|
* by a random time between 0 and
|
|
* MAX_ANYCAST_DELAY_TIME seconds.
|
|
- * (RFC2461) -- yoshfuji
|
|
*/
|
|
struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
|
|
if (n)
|
|
@@ -825,7 +837,7 @@
|
|
|
|
ipv6_addr_all_nodes(&maddr);
|
|
ndisc_send_na(dev, NULL, &maddr, &msg->target,
|
|
- idev->cnf.forwarding, 0, (ifp != NULL), 1);
|
|
+ idev->cnf.forwarding, 0, (ifp != NULL) && inc, inc);
|
|
goto out;
|
|
}
|
|
|
|
@@ -846,7 +858,7 @@
|
|
NEIGH_UPDATE_F_OVERRIDE);
|
|
if (neigh || !dev->hard_header) {
|
|
ndisc_send_na(dev, neigh, saddr, &msg->target,
|
|
- idev->cnf.forwarding,
|
|
+ idev->cnf.forwarding,
|
|
1, (ifp != NULL && inc), inc);
|
|
if (neigh)
|
|
neigh_release(neigh);
|
|
@@ -1021,6 +1033,7 @@
|
|
struct rt6_info *rt;
|
|
int lifetime;
|
|
struct ndisc_options ndopts;
|
|
+ int pref = 0;
|
|
int optlen;
|
|
|
|
__u8 * opt = (__u8 *)(ra_msg + 1);
|
|
@@ -1082,7 +1095,18 @@
|
|
|
|
lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);
|
|
|
|
+#ifdef CONFIG_IPV6_ROUTER_PREF
|
|
+ pref = IPV6_SIGNEDPREF(ra_msg->icmph.icmp6_router_pref);
|
|
+ if (pref < -1) {
|
|
+ if (net_ratelimit())
|
|
+ ND_PRINTK2("ICMP6 RA: invalid RA preference; zero lifetime\n");
|
|
+ lifetime = 0;
|
|
+ }
|
|
+#endif
|
|
+
|
|
rt = rt6_get_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
|
|
+ if (rt)
|
|
+ neigh = rt->rt6i_nexthop;
|
|
|
|
if (rt)
|
|
neigh = rt->rt6i_nexthop;
|
|
@@ -1097,7 +1121,7 @@
|
|
ND_PRINTK3(KERN_DEBUG
|
|
"ICMPv6 RA: adding default router.\n");
|
|
|
|
- rt = rt6_add_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);
|
|
+ rt = rt6_add_dflt_router(&skb->nh.ipv6h->saddr, skb->dev, pref);
|
|
if (rt == NULL) {
|
|
ND_PRINTK0(KERN_ERR
|
|
"ICMPv6 RA: %s() failed to add default route.\n",
|
|
@@ -1121,8 +1145,11 @@
|
|
if (rt)
|
|
rt->rt6i_expires = jiffies + (HZ * lifetime);
|
|
|
|
- if (ra_msg->icmph.icmp6_hop_limit)
|
|
+ if (ra_msg->icmph.icmp6_hop_limit) {
|
|
in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;
|
|
+ if (rt)
|
|
+ rt->u.dst.metrics[RTAX_HOPLIMIT-1] = ra_msg->icmph.icmp6_hop_limit;
|
|
+ }
|
|
|
|
/*
|
|
* Update Reachable Time and Retrans Timer
|
|
@@ -1329,6 +1356,7 @@
|
|
int rd_len;
|
|
int err;
|
|
int hlen;
|
|
+ u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;
|
|
|
|
dev = skb->dev;
|
|
|
|
@@ -1341,10 +1369,14 @@
|
|
|
|
ndisc_flow_init(&fl, NDISC_REDIRECT, &saddr_buf, &skb->nh.ipv6h->saddr);
|
|
|
|
- rt = rt6_lookup(&skb->nh.ipv6h->saddr, NULL, dev->ifindex, 1);
|
|
- if (rt == NULL)
|
|
+ /*
|
|
+ * we use ip6_route_output() here so that we do not try to
|
|
+ * send redirect to off-link.
|
|
+ * cf) ndisc_dst_alloc() assumes that destination is on-link.
|
|
+ */
|
|
+ dst = ip6_route_output(NULL, &fl);
|
|
+ if (dst == NULL)
|
|
return;
|
|
- dst = &rt->u.dst;
|
|
|
|
err = xfrm_lookup(&dst, &fl, NULL, 0);
|
|
if (err) {
|
|
@@ -1366,16 +1398,14 @@
|
|
}
|
|
|
|
if (dev->addr_len) {
|
|
+ read_lock_bh(&neigh->lock);
|
|
if (neigh->nud_state&NUD_VALID) {
|
|
- len += NDISC_OPT_SPACE(dev->addr_len);
|
|
- } else {
|
|
- /* If nexthop is not valid, do not redirect!
|
|
- We will make it later, when will be sure,
|
|
- that it is alive.
|
|
- */
|
|
- dst_release(dst);
|
|
- return;
|
|
- }
|
|
+ memcpy(ha_buf, neigh->ha, dev->addr_len);
|
|
+ read_unlock_bh(&neigh->lock);
|
|
+ ha = ha_buf;
|
|
+ len += NDISC_OPT_SPACE(dev->addr_len);
|
|
+ } else
|
|
+ read_unlock_bh(&neigh->lock);
|
|
}
|
|
|
|
rd_len = min_t(unsigned int,
|
|
@@ -1383,7 +1413,7 @@
|
|
rd_len &= ~0x7;
|
|
len += rd_len;
|
|
|
|
- buff = sock_alloc_send_skb(sk, MAX_HEADER + len + LL_RESERVED_SPACE(dev),
|
|
+ buff = sock_alloc_send_skb(sk, MAX_HEADER + len + LL_RESERVED_SPACE(dev) + dst->header_len + 64,
|
|
1, &err);
|
|
if (buff == NULL) {
|
|
ND_PRINTK0(KERN_ERR
|
|
@@ -1420,8 +1450,8 @@
|
|
* include target_address option
|
|
*/
|
|
|
|
- if (dev->addr_len)
|
|
- opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, neigh->ha,
|
|
+ if (ha)
|
|
+ opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha,
|
|
dev->addr_len, dev->type);
|
|
|
|
/*
|
|
@@ -1441,7 +1471,11 @@
|
|
|
|
buff->dst = dst;
|
|
idev = in6_dev_get(dst->dev);
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS);
|
|
+#endif
|
|
err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, buff, NULL, dst->dev, dst_output);
|
|
if (!err) {
|
|
ICMP6_INC_STATS(idev, ICMP6_MIB_OUTREDIRECTS);
|
|
@@ -1533,10 +1567,46 @@
|
|
};
|
|
|
|
#ifdef CONFIG_SYSCTL
|
|
+static void __ndisc_ifinfo_notifier(struct inet6_dev *idev)
|
|
+{
|
|
+ idev->tstamp = jiffies;
|
|
+ inet6_ifinfo_notify(RTM_NEWLINK, idev);
|
|
+}
|
|
+
|
|
+static void ndisc_ifinfo_notifier(void *data)
|
|
+{
|
|
+ struct net_device *dev = data;
|
|
+ if (dev) {
|
|
+ struct inet6_dev *idev = in6_dev_get(dev);
|
|
+ if (idev) {
|
|
+ __ndisc_ifinfo_notifier(idev);
|
|
+ in6_dev_put(idev);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, struct file * filp, void __user *buffer, size_t *lenp, loff_t *ppos)
|
|
{
|
|
struct net_device *dev = ctl->extra1;
|
|
struct inet6_dev *idev;
|
|
+#if 1
|
|
+ static char warncomm[TASK_COMM_LEN];
|
|
+ static int warned;
|
|
+ const char *dev_name = dev ? dev->name : "default";
|
|
+
|
|
+ if (strcmp(warncomm, current->comm) && warned < 5) {
|
|
+ strcpy(warncomm, current->comm);
|
|
+ printk(KERN_WARNING
|
|
+ "process `%s' is using old sysctl "
|
|
+ "net.ipv6.neigh.%s.%s; "
|
|
+ "Use net.ipv6.neigh.%s.%s_ms "
|
|
+ "instead.\n",
|
|
+ warncomm,
|
|
+ dev_name, ctl->procname,
|
|
+ dev_name, ctl->procname);
|
|
+ warned++;
|
|
+ }
|
|
+#endif
|
|
|
|
if (write && dev && (idev = in6_dev_get(dev)) != NULL) {
|
|
idev->tstamp = jiffies;
|
|
@@ -1578,7 +1648,8 @@
|
|
|
|
#ifdef CONFIG_SYSCTL
|
|
neigh_sysctl_register(NULL, &nd_tbl.parms, NET_IPV6, NET_IPV6_NEIGH,
|
|
- "ipv6", &ndisc_ifinfo_sysctl_change);
|
|
+ "ipv6", &ndisc_ifinfo_sysctl_change,
|
|
+ ndisc_ifinfo_notifier);
|
|
#endif
|
|
|
|
register_netdevice_notifier(&ndisc_netdev_notifier);
|
|
diff -urN linux-2.6.11/net/ipv6/netfilter/Kconfig x1/net/ipv6/netfilter/Kconfig
|
|
--- linux-2.6.11/net/ipv6/netfilter/Kconfig 2005-03-02 08:38:25.000000000 +0100
|
|
+++ x1/net/ipv6/netfilter/Kconfig 2005-02-03 05:44:15.000000000 +0100
|
|
@@ -5,6 +5,16 @@
|
|
menu "IPv6: Netfilter Configuration"
|
|
depends on INET && IPV6 && NETFILTER
|
|
|
|
+config IP6_NF_FTP
|
|
+ tristate "FTP protocol support"
|
|
+ depends on IP6_NF_CONNTRACK
|
|
+ help
|
|
+ Tracking FTP connections is problematic: special helpers are
|
|
+ required for tracking them.
|
|
+
|
|
+ If you want to compile it as a module, say M here and read
|
|
+ <file:Documentation/modules.txt>. If unsure, say `Y'.
|
|
+
|
|
#tristate 'Connection tracking (required for masq/NAT)' CONFIG_IP6_NF_CONNTRACK
|
|
#if [ "$CONFIG_IP6_NF_CONNTRACK" != "n" ]; then
|
|
# dep_tristate ' FTP protocol support' CONFIG_IP6_NF_FTP $CONFIG_IP6_NF_CONNTRACK
|
|
@@ -167,6 +177,32 @@
|
|
|
|
To compile it as a module, choose M here. If unsure, say N.
|
|
|
|
+config IP6_NF_CONNTRACK
|
|
+ tristate "Connection tracking (EXPERIMENTAL)"
|
|
+ depends on EXPERIMENTAL
|
|
+ ---help---
|
|
+ Connection tracking keeps a record of what packets have passed
|
|
+ through your machine, in order to figure out how they are related
|
|
+ into connections.
|
|
+
|
|
+ It can also be used to enhance packet filtering
|
|
+ (see `Connection state match support'
|
|
+ below).
|
|
+
|
|
+ If you want to compile it as a module, say M here and read
|
|
+ <file:Documentation/modules.txt>. If unsure, say `N'.
|
|
+
|
|
+config IP6_NF_MATCH_STATE
|
|
+ tristate "Connection state match support"
|
|
+ depends on IP6_NF_CONNTRACK && IP6_NF_IPTABLES
|
|
+ help
|
|
+ Connection state matching allows you to match packets based on their
|
|
+ relationship to a tracked connection (ie. previous packets). This
|
|
+ is a powerful tool for packet classification.
|
|
+
|
|
+ If you want to compile it as a module, say M here and read
|
|
+ <file:Documentation/modules.txt>. If unsure, say `N'.
|
|
+
|
|
# dep_tristate ' Multiple port match support' CONFIG_IP6_NF_MATCH_MULTIPORT $CONFIG_IP6_NF_IPTABLES
|
|
# dep_tristate ' TOS match support' CONFIG_IP6_NF_MATCH_TOS $CONFIG_IP6_NF_IPTABLES
|
|
# if [ "$CONFIG_IP6_NF_CONNTRACK" != "n" ]; then
|
|
@@ -196,8 +232,17 @@
|
|
|
|
To compile it as a module, choose M here. If unsure, say N.
|
|
|
|
+config IP6_NF_TARGET_REJECT
|
|
+ tristate "REJECT target support"
|
|
+ depends on IP6_NF_FILTER
|
|
+ help
|
|
+ The REJECT target allows a filtering rule to specify that an ICMP
|
|
+ error should be issued in response to an incoming packet, rather
|
|
+ than silently being dropped.
|
|
+
|
|
+ To compile it as a module, choose M here. If unsure, say N.
|
|
+
|
|
# if [ "$CONFIG_IP6_NF_FILTER" != "n" ]; then
|
|
-# dep_tristate ' REJECT target support' CONFIG_IP6_NF_TARGET_REJECT $CONFIG_IP6_NF_FILTER
|
|
# if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
|
|
# dep_tristate ' MIRROR target support (EXPERIMENTAL)' CONFIG_IP6_NF_TARGET_MIRROR $CONFIG_IP6_NF_FILTER
|
|
# fi
|
|
diff -urN linux-2.6.11/net/ipv6/netfilter/Makefile x1/net/ipv6/netfilter/Makefile
|
|
--- linux-2.6.11/net/ipv6/netfilter/Makefile 2005-03-02 08:38:10.000000000 +0100
|
|
+++ x1/net/ipv6/netfilter/Makefile 2004-09-30 15:26:36.000000000 +0200
|
|
@@ -2,6 +2,18 @@
|
|
# Makefile for the netfilter modules on top of IPv6.
|
|
#
|
|
|
|
+# objects for the conntrack
|
|
+ip6_nf_conntrack-objs := ip6_conntrack_core.o ip6_conntrack_proto_generic.o ip6_conntrack_proto_tcp.o ip6_conntrack_proto_udp.o ip6_conntrack_proto_icmpv6.o ip6_conntrack_reasm.o
|
|
+
|
|
+# objects for the standalone - connection tracking
|
|
+ip6_conntrack-objs := ip6_conntrack_standalone.o $(ip6_nf_conntrack-objs)
|
|
+
|
|
+# connection tracking
|
|
+obj-$(CONFIG_IP6_NF_CONNTRACK) += ip6_conntrack.o
|
|
+
|
|
+# connection tracking helpers
|
|
+obj-$(CONFIG_IP6_NF_FTP) += ip6_conntrack_ftp.o
|
|
+
|
|
# Link order matters here.
|
|
obj-$(CONFIG_IP6_NF_IPTABLES) += ip6_tables.o
|
|
obj-$(CONFIG_IP6_NF_MATCH_LIMIT) += ip6t_limit.o
|
|
@@ -24,3 +36,5 @@
|
|
obj-$(CONFIG_IP6_NF_TARGET_LOG) += ip6t_LOG.o
|
|
obj-$(CONFIG_IP6_NF_RAW) += ip6table_raw.o
|
|
obj-$(CONFIG_IP6_NF_MATCH_HL) += ip6t_hl.o
|
|
+obj-$(CONFIG_IP6_NF_MATCH_STATE) += ip6t_state.o
|
|
+obj-$(CONFIG_IP6_NF_TARGET_REJECT) += ip6t_REJECT.o
|
|
diff -urN linux-2.6.11/net/ipv6/netfilter/ip6_conntrack_core.c x1/net/ipv6/netfilter/ip6_conntrack_core.c
|
|
--- linux-2.6.11/net/ipv6/netfilter/ip6_conntrack_core.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/net/ipv6/netfilter/ip6_conntrack_core.c 2005-03-01 04:42:08.000000000 +0100
|
|
@@ -0,0 +1,1593 @@
|
|
+/*
|
|
+ * IPv6 Connection Tracking
|
|
+ * Linux INET6 implementation
|
|
+ *
|
|
+ * Copyright (C)2003 USAGI/WIDE Project
|
|
+ *
|
|
+ * Authors:
|
|
+ * Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
|
|
+ *
|
|
+ * Based on: net/ipv4/netfilter/ip_conntrack_core.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.
|
|
+ */
|
|
+
|
|
+/* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General
|
|
+ * Public Licence.
|
|
+ *
|
|
+ * 23 Apr 2001: Harald Welte <laforge@gnumonks.org>
|
|
+ * - new API and handling of conntrack/nat helpers
|
|
+ * - now capable of multiple expectations for one master
|
|
+ * 16 Jul 2002: Harald Welte <laforge@gnumonks.org>
|
|
+ * - add usage/reference counts to ip_conntrack_expect
|
|
+ * - export ip_conntrack[_expect]_{find_get,put} functions
|
|
+ * */
|
|
+
|
|
+#include <linux/version.h>
|
|
+#include <linux/config.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/icmpv6.h>
|
|
+#include <linux/ipv6.h>
|
|
+#include <linux/netfilter.h>
|
|
+#include <linux/netfilter_ipv6.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/skbuff.h>
|
|
+#include <linux/proc_fs.h>
|
|
+#include <linux/vmalloc.h>
|
|
+#include <net/checksum.h>
|
|
+#include <linux/stddef.h>
|
|
+#include <linux/sysctl.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/random.h>
|
|
+#include <linux/jhash.h>
|
|
+#include <net/ipv6.h>
|
|
+#include <linux/err.h>
|
|
+#include <linux/kernel.h>
|
|
+
|
|
+/* This rwlock protects the main hash table, protocol/helper/expected
|
|
+ registrations, conntrack timers*/
|
|
+#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip6_conntrack_lock)
|
|
+#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip6_conntrack_lock)
|
|
+
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack.h>
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack_protocol.h>
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack_helper.h>
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack_core.h>
|
|
+#include <linux/netfilter_ipv4/listhelp.h>
|
|
+
|
|
+#define IP6_CONNTRACK_VERSION "0.1"
|
|
+
|
|
+#if 0
|
|
+#define DEBUGP printk
|
|
+#else
|
|
+#define DEBUGP(format, args...)
|
|
+#endif
|
|
+
|
|
+DECLARE_RWLOCK(ip6_conntrack_lock);
|
|
+DECLARE_RWLOCK(ip6_conntrack_expect_tuple_lock);
|
|
+
|
|
+void (*ip6_conntrack_destroyed)(struct ip6_conntrack *conntrack) = NULL;
|
|
+LIST_HEAD(ip6_conntrack_expect_list);
|
|
+LIST_HEAD(ip6_protocol_list);
|
|
+static LIST_HEAD(helpers);
|
|
+unsigned int ip6_conntrack_htable_size = 0;
|
|
+static int ip6_conntrack_max = 0;
|
|
+static atomic_t ip6_conntrack_count = ATOMIC_INIT(0);
|
|
+struct list_head *ip6_conntrack_hash;
|
|
+static kmem_cache_t *ip6_conntrack_cachep;
|
|
+
|
|
+extern struct ip6_conntrack_protocol ip6_conntrack_generic_protocol;
|
|
+
|
|
+/*
|
|
+ * Based on ipv6_skip_exthdr() in net/ipv6/exthdr.c
|
|
+ *
|
|
+ * This function parses (probably truncated) exthdr set "hdr"
|
|
+ * of length "len". "nexthdrp" initially points to some place,
|
|
+ * where type of the first header can be found.
|
|
+ *
|
|
+ * It skips all well-known exthdrs, and returns pointer to the start
|
|
+ * of unparsable area i.e. the first header with unknown type.
|
|
+ * if success, *nexthdr is updated by type/protocol of this header.
|
|
+ *
|
|
+ * NOTES: - it may return pointer pointing beyond end of packet,
|
|
+ * if the last recognized header is truncated in the middle.
|
|
+ * - if packet is truncated, so that all parsed headers are skipped,
|
|
+ * it returns -1.
|
|
+ * - First fragment header is skipped, not-first ones
|
|
+ * are considered as unparsable.
|
|
+ * - ESP is unparsable for now and considered like
|
|
+ * normal payload protocol.
|
|
+ * - Note also special handling of AUTH header. Thanks to IPsec wizards.
|
|
+ */
|
|
+
|
|
+static int ip6_ct_skip_exthdr(struct sk_buff *skb, int start, u8 *nexthdrp,
|
|
+ int len)
|
|
+{
|
|
+ u8 nexthdr = *nexthdrp;
|
|
+
|
|
+ while (ipv6_ext_hdr(nexthdr)) {
|
|
+ struct ipv6_opt_hdr hdr;
|
|
+ int hdrlen;
|
|
+
|
|
+ if (len < (int)sizeof(struct ipv6_opt_hdr))
|
|
+ return -1;
|
|
+ if (nexthdr == NEXTHDR_NONE)
|
|
+ break;
|
|
+ if (skb_copy_bits(skb, start, &hdr, sizeof(hdr)))
|
|
+ BUG();
|
|
+ if (nexthdr == NEXTHDR_FRAGMENT) {
|
|
+ struct frag_hdr fhdr;
|
|
+
|
|
+ if (len < (int)sizeof(struct frag_hdr))
|
|
+ return -1;
|
|
+ if (skb_copy_bits(skb, start, &fhdr, sizeof(fhdr)))
|
|
+ BUG();
|
|
+ if (ntohs(fhdr.frag_off) & ~0x7)
|
|
+ return -1;
|
|
+ hdrlen = 8;
|
|
+ } else if (nexthdr == NEXTHDR_AUTH)
|
|
+ hdrlen = (hdr.hdrlen+2)<<2;
|
|
+ else
|
|
+ hdrlen = ipv6_optlen(&hdr);
|
|
+
|
|
+ nexthdr = hdr.nexthdr;
|
|
+ len -= hdrlen;
|
|
+ start += hdrlen;
|
|
+ }
|
|
+
|
|
+ *nexthdrp = nexthdr;
|
|
+ return start;
|
|
+}
|
|
+
|
|
+int ip6_ct_tuple_src_equal(const struct ip6_conntrack_tuple *t1,
|
|
+ const struct ip6_conntrack_tuple *t2)
|
|
+{
|
|
+ if (ipv6_addr_cmp(&t1->src.ip, &t2->src.ip))
|
|
+ return 0;
|
|
+
|
|
+ if (t1->src.u.all != t2->src.u.all)
|
|
+ return 0;
|
|
+
|
|
+ if (t1->dst.protonum != t2->dst.protonum)
|
|
+ return 0;
|
|
+
|
|
+ return 1;
|
|
+
|
|
+}
|
|
+
|
|
+int ip6_ct_tuple_dst_equal(const struct ip6_conntrack_tuple *t1,
|
|
+ const struct ip6_conntrack_tuple *t2)
|
|
+{
|
|
+ if (ipv6_addr_cmp(&t1->dst.ip, &t2->dst.ip))
|
|
+ return 0;
|
|
+
|
|
+ if (t1->dst.u.all != t2->dst.u.all)
|
|
+ return 0;
|
|
+
|
|
+ if (t1->dst.protonum != t2->dst.protonum)
|
|
+ return 0;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+int ip6_ct_tuple_equal(const struct ip6_conntrack_tuple *t1,
|
|
+ const struct ip6_conntrack_tuple *t2)
|
|
+{
|
|
+ return ip6_ct_tuple_src_equal(t1, t2) && ip6_ct_tuple_dst_equal(t1, t2);
|
|
+}
|
|
+
|
|
+int ip6_ct_tuple_mask_cmp(const struct ip6_conntrack_tuple *t,
|
|
+ const struct ip6_conntrack_tuple *tuple,
|
|
+ const struct ip6_conntrack_tuple *mask)
|
|
+{
|
|
+ int count = 0;
|
|
+
|
|
+ for (count = 0; count < 8; count++){
|
|
+ if ((ntohs(t->src.ip.s6_addr16[count]) ^
|
|
+ ntohs(tuple->src.ip.s6_addr16[count])) &
|
|
+ ntohs(mask->src.ip.s6_addr16[count]))
|
|
+ return 0;
|
|
+
|
|
+ if ((ntohs(t->dst.ip.s6_addr16[count]) ^
|
|
+ ntohs(tuple->dst.ip.s6_addr16[count])) &
|
|
+ ntohs(mask->dst.ip.s6_addr16[count]))
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if ((t->src.u.all ^ tuple->src.u.all) & mask->src.u.all)
|
|
+ return 0;
|
|
+
|
|
+ if ((t->dst.u.all ^ tuple->dst.u.all) & mask->dst.u.all)
|
|
+ return 0;
|
|
+
|
|
+ if ((t->dst.protonum ^ tuple->dst.protonum) & mask->dst.protonum)
|
|
+ return 0;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static inline int proto_cmpfn(const struct ip6_conntrack_protocol *curr,
|
|
+ u_int8_t protocol)
|
|
+{
|
|
+ return protocol == curr->proto;
|
|
+}
|
|
+
|
|
+struct ip6_conntrack_protocol *__ip6_ct_find_proto(u_int8_t protocol)
|
|
+{
|
|
+ struct ip6_conntrack_protocol *p;
|
|
+
|
|
+ MUST_BE_READ_LOCKED(&ip6_conntrack_lock);
|
|
+ p = LIST_FIND(&ip6_protocol_list, proto_cmpfn,
|
|
+ struct ip6_conntrack_protocol *, protocol);
|
|
+ if (!p)
|
|
+ p = &ip6_conntrack_generic_protocol;
|
|
+
|
|
+ return p;
|
|
+}
|
|
+
|
|
+struct ip6_conntrack_protocol *ip6_ct_find_proto(u_int8_t protocol)
|
|
+{
|
|
+ struct ip6_conntrack_protocol *p;
|
|
+
|
|
+ READ_LOCK(&ip6_conntrack_lock);
|
|
+ p = __ip6_ct_find_proto(protocol);
|
|
+ READ_UNLOCK(&ip6_conntrack_lock);
|
|
+ return p;
|
|
+}
|
|
+
|
|
+inline void
|
|
+ip6_conntrack_put(struct ip6_conntrack *ct)
|
|
+{
|
|
+ IP6_NF_ASSERT(ct);
|
|
+ nf_conntrack_put(&ct->ct_general);
|
|
+}
|
|
+
|
|
+static int ip6_conntrack_hash_rnd_initted;
|
|
+static unsigned int ip6_conntrack_hash_rnd;
|
|
+static u_int32_t
|
|
+hash_conntrack(const struct ip6_conntrack_tuple *tuple)
|
|
+{
|
|
+ u32 a, b, c;
|
|
+
|
|
+ a = tuple->src.ip.s6_addr32[0];
|
|
+ b = tuple->src.ip.s6_addr32[1];
|
|
+ c = tuple->src.ip.s6_addr32[2];
|
|
+
|
|
+ a += JHASH_GOLDEN_RATIO;
|
|
+ b += JHASH_GOLDEN_RATIO;
|
|
+ c += ip6_conntrack_hash_rnd;
|
|
+ __jhash_mix(a, b, c);
|
|
+
|
|
+ a += tuple->src.ip.s6_addr32[3];
|
|
+ b += tuple->dst.ip.s6_addr32[0];
|
|
+ c += tuple->dst.ip.s6_addr32[1];
|
|
+ __jhash_mix(a, b, c);
|
|
+
|
|
+ a += tuple->dst.ip.s6_addr32[2];
|
|
+ b += tuple->dst.ip.s6_addr32[3];
|
|
+ c += tuple->src.u.all | (tuple->dst.u.all << 16);
|
|
+ __jhash_mix(a, b, c);
|
|
+
|
|
+ a += tuple->dst.protonum;
|
|
+ __jhash_mix(a, b, c);
|
|
+
|
|
+ return c % ip6_conntrack_htable_size;
|
|
+}
|
|
+
|
|
+int
|
|
+ip6_get_tuple(const struct ipv6hdr *ipv6h,
|
|
+ const struct sk_buff *skb,
|
|
+ unsigned int dataoff,
|
|
+ u_int8_t protonum,
|
|
+ struct ip6_conntrack_tuple *tuple,
|
|
+ const struct ip6_conntrack_protocol *protocol)
|
|
+{
|
|
+ /* Should I check that this packet is'nt fragmented
|
|
+ like IPv4 conntrack? - kozakai */
|
|
+
|
|
+ ipv6_addr_copy(&tuple->src.ip, &ipv6h->saddr);
|
|
+ ipv6_addr_copy(&tuple->dst.ip, &ipv6h->daddr);
|
|
+
|
|
+ tuple->dst.protonum = protonum;
|
|
+
|
|
+ return protocol->pkt_to_tuple(skb, dataoff, tuple);
|
|
+}
|
|
+
|
|
+static int
|
|
+invert_tuple(struct ip6_conntrack_tuple *inverse,
|
|
+ const struct ip6_conntrack_tuple *orig,
|
|
+ const struct ip6_conntrack_protocol *protocol)
|
|
+{
|
|
+ ipv6_addr_copy(&inverse->src.ip, &orig->dst.ip);
|
|
+ ipv6_addr_copy(&inverse->dst.ip, &orig->src.ip);
|
|
+ inverse->dst.protonum = orig->dst.protonum;
|
|
+
|
|
+ return protocol->invert_tuple(inverse, orig);
|
|
+}
|
|
+
|
|
+
|
|
+/* ip6_conntrack_expect helper functions */
|
|
+
|
|
+/* Compare tuple parts depending on mask. */
|
|
+static inline int expect_cmp(const struct ip6_conntrack_expect *i,
|
|
+ const struct ip6_conntrack_tuple *tuple)
|
|
+{
|
|
+ MUST_BE_READ_LOCKED(&ip6_conntrack_expect_tuple_lock);
|
|
+ return ip6_ct_tuple_mask_cmp(tuple, &i->tuple, &i->mask);
|
|
+}
|
|
+
|
|
+static void
|
|
+destroy_expect(struct ip6_conntrack_expect *exp)
|
|
+{
|
|
+ DEBUGP("destroy_expect(%p) use=%d\n", exp, atomic_read(&exp->use));
|
|
+ IP6_NF_ASSERT(atomic_read(&exp->use));
|
|
+ IP6_NF_ASSERT(!timer_pending(&exp->timeout));
|
|
+
|
|
+ kfree(exp);
|
|
+}
|
|
+
|
|
+
|
|
+inline void ip6_conntrack_expect_put(struct ip6_conntrack_expect *exp)
|
|
+{
|
|
+ IP6_NF_ASSERT(exp);
|
|
+
|
|
+ if (atomic_dec_and_test(&exp->use)) {
|
|
+ /* usage count dropped to zero */
|
|
+ destroy_expect(exp);
|
|
+ }
|
|
+}
|
|
+
|
|
+static inline struct ip6_conntrack_expect *
|
|
+__ip6_ct_expect_find(const struct ip6_conntrack_tuple *tuple)
|
|
+{
|
|
+ MUST_BE_READ_LOCKED(&ip6_conntrack_lock);
|
|
+ MUST_BE_READ_LOCKED(&ip6_conntrack_expect_tuple_lock);
|
|
+ return LIST_FIND(&ip6_conntrack_expect_list, expect_cmp,
|
|
+ struct ip6_conntrack_expect *, tuple);
|
|
+}
|
|
+
|
|
+/* Find a expectation corresponding to a tuple. */
|
|
+struct ip6_conntrack_expect *
|
|
+ip6_conntrack_expect_find_get(const struct ip6_conntrack_tuple *tuple)
|
|
+{
|
|
+ struct ip6_conntrack_expect *exp;
|
|
+
|
|
+ READ_LOCK(&ip6_conntrack_lock);
|
|
+ READ_LOCK(&ip6_conntrack_expect_tuple_lock);
|
|
+ exp = __ip6_ct_expect_find(tuple);
|
|
+ if (exp)
|
|
+ atomic_inc(&exp->use);
|
|
+ READ_UNLOCK(&ip6_conntrack_expect_tuple_lock);
|
|
+ READ_UNLOCK(&ip6_conntrack_lock);
|
|
+
|
|
+ return exp;
|
|
+}
|
|
+
|
|
+/* remove one specific expectation from all lists and drop refcount,
|
|
+ * does _NOT_ delete the timer. */
|
|
+static void __unexpect_related(struct ip6_conntrack_expect *expect)
|
|
+{
|
|
+ DEBUGP("unexpect_related(%p)\n", expect);
|
|
+ MUST_BE_WRITE_LOCKED(&ip6_conntrack_lock);
|
|
+
|
|
+ /* we're not allowed to unexpect a confirmed expectation! */
|
|
+ IP6_NF_ASSERT(!expect->sibling);
|
|
+
|
|
+ /* delete from global and local lists */
|
|
+ list_del(&expect->list);
|
|
+ list_del(&expect->expected_list);
|
|
+
|
|
+ /* decrement expect-count of master conntrack */
|
|
+ if (expect->expectant)
|
|
+ expect->expectant->expecting--;
|
|
+
|
|
+ ip6_conntrack_expect_put(expect);
|
|
+}
|
|
+
|
|
+/* remove one specific expecatation from all lists, drop refcount
|
|
+ * and expire timer.
|
|
+ * This function can _NOT_ be called for confirmed expects! */
|
|
+static void unexpect_related(struct ip6_conntrack_expect *expect)
|
|
+{
|
|
+ IP6_NF_ASSERT(expect->expectant);
|
|
+ IP6_NF_ASSERT(expect->expectant->helper);
|
|
+ /* if we are supposed to have a timer, but we can't delete
|
|
+ * it: race condition. __unexpect_related will
|
|
+ * be calledd by timeout function */
|
|
+ if (expect->expectant->helper->timeout
|
|
+ && !del_timer(&expect->timeout))
|
|
+ return;
|
|
+
|
|
+ __unexpect_related(expect);
|
|
+}
|
|
+
|
|
+/* delete all unconfirmed expectations for this conntrack */
|
|
+static void remove_expectations(struct ip6_conntrack *ct, int drop_refcount)
|
|
+{
|
|
+ struct list_head *exp_entry, *next;
|
|
+ struct ip6_conntrack_expect *exp;
|
|
+
|
|
+ DEBUGP("remove_expectations(%p)\n", ct);
|
|
+
|
|
+ list_for_each_safe(exp_entry, next, &ct->sibling_list) {
|
|
+ exp = list_entry(exp_entry, struct ip6_conntrack_expect,
|
|
+ expected_list);
|
|
+
|
|
+ /* we skip established expectations, as we want to delete
|
|
+ * the un-established ones only */
|
|
+ if (exp->sibling) {
|
|
+ DEBUGP("remove_expectations: skipping established %p of %p\n", exp->sibling, ct);
|
|
+ if (drop_refcount) {
|
|
+ /* Indicate that this expectations parent is dead */
|
|
+ ip6_conntrack_put(exp->expectant);
|
|
+ exp->expectant = NULL;
|
|
+ }
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ IP6_NF_ASSERT(list_inlist(&ip6_conntrack_expect_list, exp));
|
|
+ IP6_NF_ASSERT(exp->expectant == ct);
|
|
+
|
|
+ /* delete expectation from global and private lists */
|
|
+ unexpect_related(exp);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void
|
|
+clean_from_lists(struct ip6_conntrack *ct)
|
|
+{
|
|
+ unsigned int ho, hr;
|
|
+
|
|
+ DEBUGP("clean_from_lists(%p)\n", ct);
|
|
+ MUST_BE_WRITE_LOCKED(&ip6_conntrack_lock);
|
|
+
|
|
+ ho = hash_conntrack(&ct->tuplehash[IP6_CT_DIR_ORIGINAL].tuple);
|
|
+ hr = hash_conntrack(&ct->tuplehash[IP6_CT_DIR_REPLY].tuple);
|
|
+
|
|
+ LIST_DELETE(&ip6_conntrack_hash[ho],
|
|
+ &ct->tuplehash[IP6_CT_DIR_ORIGINAL]);
|
|
+ LIST_DELETE(&ip6_conntrack_hash[hr],
|
|
+ &ct->tuplehash[IP6_CT_DIR_REPLY]);
|
|
+
|
|
+ /* Destroy all un-established, pending expectations */
|
|
+ remove_expectations(ct, 1);
|
|
+}
|
|
+
|
|
+static void
|
|
+destroy_conntrack(struct nf_conntrack *nfct)
|
|
+{
|
|
+ struct ip6_conntrack *ct = (struct ip6_conntrack *)nfct, *master = NULL;
|
|
+ struct ip6_conntrack_protocol *proto;
|
|
+
|
|
+ DEBUGP("destroy_conntrack(%p)\n", ct);
|
|
+ IP6_NF_ASSERT(atomic_read(&nfct->use) == 0);
|
|
+ IP6_NF_ASSERT(!timer_pending(&ct->timeout));
|
|
+
|
|
+ /* To make sure we don't get any weird locking issues here:
|
|
+ * destroy_conntrack() MUST NOT be called with a write lock
|
|
+ * to ip6_conntrack_lock!!! -HW */
|
|
+ proto = ip6_ct_find_proto(ct->tuplehash[IP6_CT_DIR_REPLY].tuple.dst.protonum);
|
|
+ if (proto && proto->destroy)
|
|
+ proto->destroy(ct);
|
|
+
|
|
+ if (ip6_conntrack_destroyed)
|
|
+ ip6_conntrack_destroyed(ct);
|
|
+
|
|
+ WRITE_LOCK(&ip6_conntrack_lock);
|
|
+ /* Delete us from our own list to prevent corruption later */
|
|
+ list_del(&ct->sibling_list);
|
|
+
|
|
+ /* Delete our master expectation */
|
|
+ if (ct->master) {
|
|
+ if (ct->master->expectant) {
|
|
+ /* can't call __unexpect_related here,
|
|
+ * since it would screw up expect_list */
|
|
+ list_del(&ct->master->expected_list);
|
|
+ master = ct->master->expectant;
|
|
+ }
|
|
+ kfree(ct->master);
|
|
+ }
|
|
+ WRITE_UNLOCK(&ip6_conntrack_lock);
|
|
+
|
|
+ if (master)
|
|
+ ip6_conntrack_put(master);
|
|
+
|
|
+ DEBUGP("destroy_conntrack: returning ct=%p to slab\n", ct);
|
|
+ kmem_cache_free(ip6_conntrack_cachep, ct);
|
|
+ atomic_dec(&ip6_conntrack_count);
|
|
+}
|
|
+
|
|
+static void death_by_timeout(unsigned long ul_conntrack)
|
|
+{
|
|
+ struct ip6_conntrack *ct = (void *)ul_conntrack;
|
|
+
|
|
+ WRITE_LOCK(&ip6_conntrack_lock);
|
|
+ clean_from_lists(ct);
|
|
+ WRITE_UNLOCK(&ip6_conntrack_lock);
|
|
+ ip6_conntrack_put(ct);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+conntrack_tuple_cmp(const struct ip6_conntrack_tuple_hash *i,
|
|
+ const struct ip6_conntrack_tuple *tuple,
|
|
+ const struct ip6_conntrack *ignored_conntrack)
|
|
+{
|
|
+ MUST_BE_READ_LOCKED(&ip6_conntrack_lock);
|
|
+ return i->ctrack != ignored_conntrack
|
|
+ && ip6_ct_tuple_equal(tuple, &i->tuple);
|
|
+}
|
|
+
|
|
+static struct ip6_conntrack_tuple_hash *
|
|
+__ip6_conntrack_find(const struct ip6_conntrack_tuple *tuple,
|
|
+ const struct ip6_conntrack *ignored_conntrack)
|
|
+{
|
|
+ struct ip6_conntrack_tuple_hash *h;
|
|
+ unsigned int hash = hash_conntrack(tuple);
|
|
+
|
|
+ MUST_BE_READ_LOCKED(&ip6_conntrack_lock);
|
|
+ h = LIST_FIND(&ip6_conntrack_hash[hash],
|
|
+ conntrack_tuple_cmp,
|
|
+ struct ip6_conntrack_tuple_hash *,
|
|
+ tuple, ignored_conntrack);
|
|
+ return h;
|
|
+}
|
|
+
|
|
+/* Find a connection corresponding to a tuple. */
|
|
+struct ip6_conntrack_tuple_hash *
|
|
+ip6_conntrack_find_get(const struct ip6_conntrack_tuple *tuple,
|
|
+ const struct ip6_conntrack *ignored_conntrack)
|
|
+{
|
|
+ struct ip6_conntrack_tuple_hash *h;
|
|
+
|
|
+ READ_LOCK(&ip6_conntrack_lock);
|
|
+ h = __ip6_conntrack_find(tuple, ignored_conntrack);
|
|
+ if (h)
|
|
+ atomic_inc(&h->ctrack->ct_general.use);
|
|
+ READ_UNLOCK(&ip6_conntrack_lock);
|
|
+
|
|
+ return h;
|
|
+}
|
|
+
|
|
+/* Confirm a connection given skb; places it in hash table */
|
|
+int __ip6_conntrack_confirm(struct sk_buff *skb)
|
|
+{
|
|
+ unsigned int hash, repl_hash;
|
|
+ struct ip6_conntrack *ct;
|
|
+ enum ip6_conntrack_info ctinfo;
|
|
+
|
|
+ ct = ip6_conntrack_get(skb, &ctinfo);
|
|
+
|
|
+ /* ip6t_REJECT uses ip6_conntrack_attach to attach related
|
|
+ ICMP/TCP RST packets in other direction. Actual packet
|
|
+ which created connection will be IP6_CT_NEW or for an
|
|
+ expected connection, IP6_CT_RELATED. */
|
|
+ if (CTINFO2DIR(ctinfo) != IP6_CT_DIR_ORIGINAL)
|
|
+ return NF_ACCEPT;
|
|
+
|
|
+ hash = hash_conntrack(&ct->tuplehash[IP6_CT_DIR_ORIGINAL].tuple);
|
|
+ repl_hash = hash_conntrack(&ct->tuplehash[IP6_CT_DIR_REPLY].tuple);
|
|
+
|
|
+ /* We're not in hash table, and we refuse to set up related
|
|
+ connections for unconfirmed conns. But packet copies and
|
|
+ REJECT will give spurious warnings here. */
|
|
+ /* IP6_NF_ASSERT(atomic_read(&ct->ct_general.use) == 1); */
|
|
+
|
|
+ /* No external references means noone else could have
|
|
+ confirmed us. */
|
|
+ IP6_NF_ASSERT(!is_confirmed(ct));
|
|
+ DEBUGP("Confirming conntrack %p\n", ct);
|
|
+
|
|
+ WRITE_LOCK(&ip6_conntrack_lock);
|
|
+ /* See if there's one in the list already, including reverse:
|
|
+ NAT could have grabbed it without realizing, since we're
|
|
+ not in the hash. If there is, we lost race. */
|
|
+ if (!LIST_FIND(&ip6_conntrack_hash[hash],
|
|
+ conntrack_tuple_cmp,
|
|
+ struct ip6_conntrack_tuple_hash *,
|
|
+ &ct->tuplehash[IP6_CT_DIR_ORIGINAL].tuple, NULL)
|
|
+ && !LIST_FIND(&ip6_conntrack_hash[repl_hash],
|
|
+ conntrack_tuple_cmp,
|
|
+ struct ip6_conntrack_tuple_hash *,
|
|
+ &ct->tuplehash[IP6_CT_DIR_REPLY].tuple, NULL)) {
|
|
+ list_prepend(&ip6_conntrack_hash[hash],
|
|
+ &ct->tuplehash[IP6_CT_DIR_ORIGINAL]);
|
|
+ list_prepend(&ip6_conntrack_hash[repl_hash],
|
|
+ &ct->tuplehash[IP6_CT_DIR_REPLY]);
|
|
+ /* Timer relative to confirmation time, not original
|
|
+ setting time, otherwise we'd get timer wrap in
|
|
+ wierd delay cases. */
|
|
+ ct->timeout.expires += jiffies;
|
|
+ add_timer(&ct->timeout);
|
|
+ atomic_inc(&ct->ct_general.use);
|
|
+ set_bit(IP6S_CONFIRMED_BIT, &ct->status);
|
|
+ WRITE_UNLOCK(&ip6_conntrack_lock);
|
|
+ return NF_ACCEPT;
|
|
+ }
|
|
+
|
|
+ WRITE_UNLOCK(&ip6_conntrack_lock);
|
|
+ return NF_DROP;
|
|
+}
|
|
+
|
|
+/* Is this needed ? this code is for NAT. - kozakai */
|
|
+/* Returns true if a connection correspondings to the tuple (required
|
|
+ for NAT). */
|
|
+int
|
|
+ip6_conntrack_tuple_taken(const struct ip6_conntrack_tuple *tuple,
|
|
+ const struct ip6_conntrack *ignored_conntrack)
|
|
+{
|
|
+ struct ip6_conntrack_tuple_hash *h;
|
|
+
|
|
+ READ_LOCK(&ip6_conntrack_lock);
|
|
+ h = __ip6_conntrack_find(tuple, ignored_conntrack);
|
|
+ READ_UNLOCK(&ip6_conntrack_lock);
|
|
+
|
|
+ return h != NULL;
|
|
+}
|
|
+
|
|
+/* Returns conntrack if it dealt with ICMP, and filled in skb fields */
|
|
+struct ip6_conntrack *
|
|
+icmp6_error_track(struct sk_buff *skb,
|
|
+ unsigned int icmp6off,
|
|
+ enum ip6_conntrack_info *ctinfo,
|
|
+ unsigned int hooknum)
|
|
+{
|
|
+ struct ip6_conntrack_tuple intuple, origtuple;
|
|
+ struct ip6_conntrack_tuple_hash *h;
|
|
+ struct ipv6hdr *ip6h;
|
|
+ struct icmp6hdr hdr;
|
|
+ struct ipv6hdr inip6h;
|
|
+ unsigned int inip6off;
|
|
+ struct ip6_conntrack_protocol *inproto;
|
|
+ u_int8_t inprotonum;
|
|
+ unsigned int inprotoff;
|
|
+
|
|
+ IP6_NF_ASSERT(skb->nfct == NULL);
|
|
+
|
|
+ ip6h = skb->nh.ipv6h;
|
|
+ if (skb_copy_bits(skb, icmp6off, &hdr, sizeof(hdr)) != 0) {
|
|
+ DEBUGP("icmp_error_track: Can't copy ICMPv6 hdr.\n");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if (hdr.icmp6_type >= 128)
|
|
+ return NULL;
|
|
+
|
|
+ /*
|
|
+ * Should I ignore invalid ICMPv6 error here ?
|
|
+ * ex) ICMPv6 error in ICMPv6 error, Fragmented packet, and so on.
|
|
+ * - kozakai
|
|
+ */
|
|
+
|
|
+ /* Why not check checksum in IPv4 conntrack ? - kozakai */
|
|
+ /* Ignore it if the checksum's bogus. */
|
|
+
|
|
+ if (csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, skb->len - icmp6off,
|
|
+ IPPROTO_ICMPV6,
|
|
+ skb_checksum(skb, icmp6off,
|
|
+ skb->len - icmp6off, 0))) {
|
|
+ DEBUGP("ICMPv6 checksum failed\n");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ inip6off = icmp6off + sizeof(hdr);
|
|
+ if (skb_copy_bits(skb, inip6off, &inip6h, sizeof(inip6h)) != 0) {
|
|
+ DEBUGP("Can't copy inner IPv6 hdr.\n");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ inprotonum = inip6h.nexthdr;
|
|
+ inprotoff = ip6_ct_skip_exthdr(skb, inip6off + sizeof(inip6h),
|
|
+ &inprotonum,
|
|
+ skb->len - inip6off - sizeof(inip6h));
|
|
+
|
|
+ if (inprotoff < 0 || inprotoff > skb->len
|
|
+ || inprotonum == NEXTHDR_FRAGMENT) {
|
|
+ DEBUGP("icmp6_error: Can't find protocol header in ICMPv6 payload.\n");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ inproto = ip6_ct_find_proto(inprotonum);
|
|
+
|
|
+ /* Are they talking about one of our connections? */
|
|
+ if (!ip6_get_tuple(&inip6h, skb, inprotoff, inprotonum,
|
|
+ &origtuple, inproto)) {
|
|
+ DEBUGP("icmp6_error: ! get_tuple p=%u\n", inprotonum);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ /* Ordinarily, we'd expect the inverted tupleproto, but it's
|
|
+ been preserved inside the ICMP. */
|
|
+ if (!invert_tuple(&intuple, &origtuple, inproto)) {
|
|
+ DEBUGP("icmp6_error_track: Can't invert tuple\n");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ *ctinfo = IP6_CT_RELATED;
|
|
+
|
|
+ h = ip6_conntrack_find_get(&intuple, NULL);
|
|
+ if (!h) {
|
|
+ DEBUGP("icmp6_error_track: no match\n");
|
|
+ return NULL;
|
|
+ } else {
|
|
+ if (DIRECTION(h) == IP6_CT_DIR_REPLY)
|
|
+ *ctinfo += IP6_CT_IS_REPLY;
|
|
+ }
|
|
+
|
|
+ /* Update skb to refer to this connection */
|
|
+ skb->nfct = &h->ctrack->ct_general;
|
|
+ return h->ctrack;
|
|
+}
|
|
+
|
|
+/* There's a small race here where we may free a just-assured
|
|
+ connection. Too bad: we're in trouble anyway. */
|
|
+static inline int unreplied(const struct ip6_conntrack_tuple_hash *i)
|
|
+{
|
|
+ return !(test_bit(IP6S_ASSURED_BIT, &i->ctrack->status));
|
|
+}
|
|
+
|
|
+static int early_drop(struct list_head *chain)
|
|
+{
|
|
+ /* Traverse backwards: gives us oldest, which is roughly LRU */
|
|
+ struct ip6_conntrack_tuple_hash *h;
|
|
+ int dropped = 0;
|
|
+
|
|
+ READ_LOCK(&ip6_conntrack_lock);
|
|
+ h = LIST_FIND_B(chain, unreplied, struct ip6_conntrack_tuple_hash *);
|
|
+ if (h)
|
|
+ atomic_inc(&h->ctrack->ct_general.use);
|
|
+ READ_UNLOCK(&ip6_conntrack_lock);
|
|
+
|
|
+ if (!h)
|
|
+ return dropped;
|
|
+
|
|
+ if (del_timer(&h->ctrack->timeout)) {
|
|
+ death_by_timeout((unsigned long)h->ctrack);
|
|
+ dropped = 1;
|
|
+ }
|
|
+ ip6_conntrack_put(h->ctrack);
|
|
+ return dropped;
|
|
+}
|
|
+
|
|
+static inline int helper_cmp(const struct ip6_conntrack_helper *i,
|
|
+ const struct ip6_conntrack_tuple *rtuple)
|
|
+{
|
|
+ return ip6_ct_tuple_mask_cmp(rtuple, &i->tuple, &i->mask);
|
|
+}
|
|
+
|
|
+struct ip6_conntrack_helper *
|
|
+ip6_ct_find_helper(const struct ip6_conntrack_tuple *tuple){
|
|
+
|
|
+ MUST_BE_READ_LOCKED(&ip6_conntrack_lock);
|
|
+ return LIST_FIND(&helpers, helper_cmp,
|
|
+ struct ip6_conntrack_helper *,
|
|
+ tuple);
|
|
+}
|
|
+
|
|
+/* Allocate a new conntrack: we return -ENOMEM if classification
|
|
+ failed due to stress. Otherwise it really is unclassifiable. */
|
|
+static struct ip6_conntrack_tuple_hash *
|
|
+init_conntrack(const struct ip6_conntrack_tuple *tuple,
|
|
+ struct ip6_conntrack_protocol *protocol,
|
|
+ struct sk_buff *skb,
|
|
+ unsigned int protoff)
|
|
+{
|
|
+ struct ip6_conntrack *conntrack;
|
|
+ struct ip6_conntrack_tuple repl_tuple;
|
|
+ size_t hash;
|
|
+ struct ip6_conntrack_expect *expected;
|
|
+ static unsigned int drop_next = 0;
|
|
+
|
|
+ if (!ip6_conntrack_hash_rnd_initted) {
|
|
+ get_random_bytes(&ip6_conntrack_hash_rnd, 4);
|
|
+ ip6_conntrack_hash_rnd_initted = 1;
|
|
+ }
|
|
+
|
|
+ hash = hash_conntrack(tuple);
|
|
+
|
|
+ if (ip6_conntrack_max &&
|
|
+ atomic_read(&ip6_conntrack_count) >= ip6_conntrack_max) {
|
|
+ /* Try dropping from random chain, or else from the
|
|
+ chain about to put into (in case they're trying to
|
|
+ bomb one hash chain). */
|
|
+ unsigned int next = (drop_next++)%ip6_conntrack_htable_size;
|
|
+
|
|
+ if (!early_drop(&ip6_conntrack_hash[next])
|
|
+ && !early_drop(&ip6_conntrack_hash[hash])) {
|
|
+ if (net_ratelimit())
|
|
+ printk(KERN_WARNING
|
|
+ "ip6_conntrack: table full, dropping"
|
|
+ " packet.\n");
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!invert_tuple(&repl_tuple, tuple, protocol)) {
|
|
+ DEBUGP("Can't invert tuple.\n");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ conntrack = kmem_cache_alloc(ip6_conntrack_cachep, GFP_ATOMIC);
|
|
+ if (!conntrack) {
|
|
+ DEBUGP("Can't allocate conntrack.\n");
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+ }
|
|
+
|
|
+ memset(conntrack, 0, sizeof(*conntrack));
|
|
+ atomic_set(&conntrack->ct_general.use, 1);
|
|
+ conntrack->ct_general.destroy = destroy_conntrack;
|
|
+ conntrack->tuplehash[IP6_CT_DIR_ORIGINAL].tuple = *tuple;
|
|
+ conntrack->tuplehash[IP6_CT_DIR_ORIGINAL].ctrack = conntrack;
|
|
+ conntrack->tuplehash[IP6_CT_DIR_REPLY].tuple = repl_tuple;
|
|
+ conntrack->tuplehash[IP6_CT_DIR_REPLY].ctrack = conntrack;
|
|
+
|
|
+ if (!protocol->new(conntrack, skb, protoff)) {
|
|
+ kmem_cache_free(ip6_conntrack_cachep, conntrack);
|
|
+ return NULL;
|
|
+ }
|
|
+ /* Don't set timer yet: wait for confirmation */
|
|
+ init_timer(&conntrack->timeout);
|
|
+ conntrack->timeout.data = (unsigned long)conntrack;
|
|
+ conntrack->timeout.function = death_by_timeout;
|
|
+
|
|
+ INIT_LIST_HEAD(&conntrack->sibling_list);
|
|
+
|
|
+ WRITE_LOCK(&ip6_conntrack_lock);
|
|
+ /* Need finding and deleting of expected ONLY if we win race */
|
|
+ READ_LOCK(&ip6_conntrack_expect_tuple_lock);
|
|
+ expected = LIST_FIND(&ip6_conntrack_expect_list, expect_cmp,
|
|
+ struct ip6_conntrack_expect *, tuple);
|
|
+ READ_UNLOCK(&ip6_conntrack_expect_tuple_lock);
|
|
+
|
|
+ /* If master is not in hash table yet (ie. packet hasn't left
|
|
+ this machine yet), how can other end know about expected?
|
|
+ Hence these are not the droids you are looking for (if
|
|
+ master ct never got confirmed, we'd hold a reference to it
|
|
+ and weird things would happen to future packets). */
|
|
+ if (expected && !is_confirmed(expected->expectant))
|
|
+ expected = NULL;
|
|
+
|
|
+ /* Look up the conntrack helper for master connections only */
|
|
+ if (!expected)
|
|
+ conntrack->helper = ip6_ct_find_helper(&repl_tuple);
|
|
+
|
|
+ /* If the expectation is dying, then this is a loser. */
|
|
+ if (expected
|
|
+ && expected->expectant->helper->timeout
|
|
+ && ! del_timer(&expected->timeout))
|
|
+ expected = NULL;
|
|
+
|
|
+ if (expected) {
|
|
+ DEBUGP("conntrack: expectation arrives ct=%p exp=%p\n",
|
|
+ conntrack, expected);
|
|
+ /* Welcome, Mr. Bond. We've been expecting you... */
|
|
+ IP6_NF_ASSERT(master_ct6(conntrack));
|
|
+ __set_bit(IP6S_EXPECTED_BIT, &conntrack->status);
|
|
+ conntrack->master = expected;
|
|
+ expected->sibling = conntrack;
|
|
+ LIST_DELETE(&ip6_conntrack_expect_list, expected);
|
|
+ expected->expectant->expecting--;
|
|
+ nf_conntrack_get(&master_ct6(conntrack)->ct_general);
|
|
+ }
|
|
+ atomic_inc(&ip6_conntrack_count);
|
|
+ WRITE_UNLOCK(&ip6_conntrack_lock);
|
|
+
|
|
+ if (expected && expected->expectfn)
|
|
+ expected->expectfn(conntrack);
|
|
+ return &conntrack->tuplehash[IP6_CT_DIR_ORIGINAL];
|
|
+}
|
|
+
|
|
+/* On success, returns conntrack ptr, sets skb->nfct and ctinfo */
|
|
+static inline struct ip6_conntrack *
|
|
+resolve_normal_ct(struct sk_buff *skb,
|
|
+ unsigned int protoff,
|
|
+ u_int16_t protonum,
|
|
+ struct ip6_conntrack_protocol *proto,
|
|
+ int *set_reply,
|
|
+ unsigned int hooknum,
|
|
+ enum ip6_conntrack_info *ctinfo)
|
|
+{
|
|
+ struct ip6_conntrack_tuple tuple;
|
|
+ struct ip6_conntrack_tuple_hash *h;
|
|
+
|
|
+ if (!ip6_get_tuple(skb->nh.ipv6h, skb, protoff, protonum, &tuple, proto))
|
|
+ return NULL;
|
|
+
|
|
+ /* look for tuple match */
|
|
+ h = ip6_conntrack_find_get(&tuple, NULL);
|
|
+ if (!h) {
|
|
+ h = init_conntrack(&tuple, proto, skb, protoff);
|
|
+ if (!h)
|
|
+ return NULL;
|
|
+ if (IS_ERR(h))
|
|
+ return (void *)h;
|
|
+ }
|
|
+
|
|
+ /* It exists; we have (non-exclusive) reference. */
|
|
+ if (DIRECTION(h) == IP6_CT_DIR_REPLY) {
|
|
+ *ctinfo = IP6_CT_ESTABLISHED + IP6_CT_IS_REPLY;
|
|
+ /* Please set reply bit if this packet OK */
|
|
+ *set_reply = 1;
|
|
+ } else {
|
|
+ /* Once we've had two way comms, always ESTABLISHED. */
|
|
+ if (test_bit(IP6S_SEEN_REPLY_BIT, &h->ctrack->status)) {
|
|
+ DEBUGP("ip6_conntrack_in: normal packet for %p\n",
|
|
+ h->ctrack);
|
|
+ *ctinfo = IP6_CT_ESTABLISHED;
|
|
+ } else if (test_bit(IP6S_EXPECTED_BIT, &h->ctrack->status)) {
|
|
+ DEBUGP("ip6_conntrack_in: related packet for %p\n",
|
|
+ h->ctrack);
|
|
+ *ctinfo = IP6_CT_RELATED;
|
|
+ } else {
|
|
+ DEBUGP("ip6_conntrack_in: new packet for %p\n",
|
|
+ h->ctrack);
|
|
+ *ctinfo = IP6_CT_NEW;
|
|
+ }
|
|
+ *set_reply = 0;
|
|
+ }
|
|
+ skb->nfct = &h->ctrack->ct_general;
|
|
+ skb->nfctinfo = *ctinfo;
|
|
+ return h->ctrack;
|
|
+}
|
|
+
|
|
+/* Netfilter hook itself. */
|
|
+unsigned int ip6_conntrack_in(unsigned int hooknum,
|
|
+ struct sk_buff **pskb,
|
|
+ const struct net_device *in,
|
|
+ const struct net_device *out,
|
|
+ int (*okfn)(struct sk_buff *))
|
|
+{
|
|
+ struct ip6_conntrack *ct;
|
|
+ enum ip6_conntrack_info ctinfo;
|
|
+ struct ip6_conntrack_protocol *proto;
|
|
+ int set_reply;
|
|
+ int ret;
|
|
+ u_int8_t protonum;
|
|
+ int len;
|
|
+ int daddr_type;
|
|
+ int protoff, extoff;
|
|
+
|
|
+ /* FIXME: Do this right please. --RR */
|
|
+ (*pskb)->nfcache |= NFC_UNKNOWN;
|
|
+
|
|
+ /* Ignore multicast - kozakai */
|
|
+ daddr_type = ipv6_addr_type(&(*pskb)->nh.ipv6h->daddr);
|
|
+ if (daddr_type & IPV6_ADDR_MULTICAST)
|
|
+ return NF_ACCEPT;
|
|
+
|
|
+ /* Previously seen (loopback)? Ignore. Do this before
|
|
+ fragment check. */
|
|
+ if ((*pskb)->nfct)
|
|
+ return NF_ACCEPT;
|
|
+
|
|
+ extoff = (u8*)((*pskb)->nh.ipv6h+1) - (*pskb)->data;
|
|
+ len = (*pskb)->len - extoff;
|
|
+
|
|
+ /* Verify that a protocol is present and get the protocol handler
|
|
+ we need */
|
|
+ protonum = (*pskb)->nh.ipv6h->nexthdr;
|
|
+ protoff = ip6_ct_skip_exthdr(*pskb, extoff, &protonum, len);
|
|
+
|
|
+ /*
|
|
+ * Notice! (protoff == (*pskb)->len) mean that this packet doesn't
|
|
+ * have no data except of IPv6 & ext headers. but tracked anyway.
|
|
+ * - kozakai
|
|
+ */
|
|
+ if (protoff < 0 || protoff > (*pskb)->len
|
|
+ || protonum == NEXTHDR_FRAGMENT) {
|
|
+ DEBUGP("ip6_conntrack_core: can't find proto in pkt\n");
|
|
+ return NF_ACCEPT;
|
|
+ }
|
|
+
|
|
+ /* It may be an icmp error... */
|
|
+ if (protonum == IPPROTO_ICMPV6
|
|
+ && icmp6_error_track(*pskb, protoff, &ctinfo, hooknum))
|
|
+ return NF_ACCEPT;
|
|
+
|
|
+ proto = ip6_ct_find_proto(protonum);
|
|
+
|
|
+ if (!(ct = resolve_normal_ct(*pskb, protoff, protonum, proto,
|
|
+ &set_reply, hooknum,&ctinfo)))
|
|
+ /* Not valid part of a connection */
|
|
+ return NF_ACCEPT;
|
|
+
|
|
+ if (IS_ERR(ct))
|
|
+ /* Too stressed to deal. */
|
|
+ return NF_DROP;
|
|
+
|
|
+ IP6_NF_ASSERT((*pskb)->nfct);
|
|
+
|
|
+ ret = proto->packet(ct, *pskb, protoff, ctinfo);
|
|
+ if (ret == -1) {
|
|
+ /* Invalid */
|
|
+ nf_conntrack_put((*pskb)->nfct);
|
|
+ (*pskb)->nfct = NULL;
|
|
+ return NF_ACCEPT;
|
|
+ }
|
|
+
|
|
+ if (ret != NF_DROP && ct->helper) {
|
|
+ ret = ct->helper->help(*pskb, protoff, ct, ctinfo);
|
|
+ if (ret == -1) {
|
|
+ /* Invalid */
|
|
+ nf_conntrack_put((*pskb)->nfct);
|
|
+ (*pskb)->nfct = NULL;
|
|
+ return NF_ACCEPT;
|
|
+ }
|
|
+ }
|
|
+ if (set_reply)
|
|
+ set_bit(IP6S_SEEN_REPLY_BIT, &ct->status);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int ip6_invert_tuplepr(struct ip6_conntrack_tuple *inverse,
|
|
+ const struct ip6_conntrack_tuple *orig)
|
|
+{
|
|
+ return invert_tuple(inverse, orig, ip6_ct_find_proto(orig->dst.protonum));
|
|
+}
|
|
+
|
|
+static inline int resent_expect(const struct ip6_conntrack_expect *i,
|
|
+ const struct ip6_conntrack_tuple *tuple,
|
|
+ const struct ip6_conntrack_tuple *mask)
|
|
+{
|
|
+ DEBUGP("resent_expect\n");
|
|
+ DEBUGP(" tuple: "); DUMP_TUPLE(&i->tuple);
|
|
+ DEBUGP("test tuple: "); DUMP_TUPLE(tuple);
|
|
+ return (ip6_ct_tuple_equal(&i->tuple, tuple)
|
|
+ && ip6_ct_tuple_equal(&i->mask, mask));
|
|
+}
|
|
+
|
|
+static struct in6_addr *
|
|
+or_addr6_bits(struct in6_addr *result, const struct in6_addr *one,
|
|
+ const struct in6_addr *two)
|
|
+{
|
|
+
|
|
+ int count = 0;
|
|
+
|
|
+ for (count = 0; count < 8; count++)
|
|
+ result->s6_addr16[count] = ntohs(one->s6_addr16[count])
|
|
+ & ntohs(two->s6_addr16[count]);
|
|
+
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/* Would two expected things clash? */
|
|
+static inline int expect_clash(const struct ip6_conntrack_expect *i,
|
|
+ const struct ip6_conntrack_tuple *tuple,
|
|
+ const struct ip6_conntrack_tuple *mask)
|
|
+{
|
|
+ /* Part covered by intersection of masks must be unequal,
|
|
+ otherwise they clash */
|
|
+ struct ip6_conntrack_tuple intersect_mask;
|
|
+
|
|
+ intersect_mask.src.u.all = i->mask.src.u.all & mask->src.u.all;
|
|
+ intersect_mask.dst.u.all = i->mask.dst.u.all & mask->dst.u.all;
|
|
+ intersect_mask.dst.protonum = i->mask.dst.protonum
|
|
+ & mask->dst.protonum;
|
|
+
|
|
+ or_addr6_bits(&intersect_mask.src.ip, &i->mask.src.ip,
|
|
+ &mask->src.ip);
|
|
+ or_addr6_bits(&intersect_mask.dst.ip, &i->mask.dst.ip,
|
|
+ &mask->dst.ip);
|
|
+
|
|
+ return ip6_ct_tuple_mask_cmp(&i->tuple, tuple, &intersect_mask);
|
|
+}
|
|
+
|
|
+inline void ip6_conntrack_unexpect_related(struct ip6_conntrack_expect *expect)
|
|
+{
|
|
+ WRITE_LOCK(&ip6_conntrack_lock);
|
|
+ unexpect_related(expect);
|
|
+ WRITE_UNLOCK(&ip6_conntrack_lock);
|
|
+}
|
|
+
|
|
+static void expectation_timed_out(unsigned long ul_expect)
|
|
+{
|
|
+ struct ip6_conntrack_expect *expect = (void *) ul_expect;
|
|
+
|
|
+ DEBUGP("expectation %p timed out\n", expect);
|
|
+ WRITE_LOCK(&ip6_conntrack_lock);
|
|
+ __unexpect_related(expect);
|
|
+ WRITE_UNLOCK(&ip6_conntrack_lock);
|
|
+}
|
|
+
|
|
+/* Add a related connection. */
|
|
+int ip6_conntrack_expect_related(struct ip6_conntrack *related_to,
|
|
+ struct ip6_conntrack_expect *expect)
|
|
+{
|
|
+ struct ip6_conntrack_expect *old, *new;
|
|
+ int ret = 0;
|
|
+
|
|
+ WRITE_LOCK(&ip6_conntrack_lock);
|
|
+ /* Because of the write lock, no reader can walk the lists,
|
|
+ * so there is no need to use the tuple lock too */
|
|
+
|
|
+ DEBUGP("ip6_conntrack_expect_related %p\n", related_to);
|
|
+ DEBUGP("tuple: "); DUMP_TUPLE(&expect->tuple);
|
|
+ DEBUGP("mask: "); DUMP_TUPLE(&expect->mask);
|
|
+
|
|
+ old = LIST_FIND(&ip6_conntrack_expect_list, resent_expect,
|
|
+ struct ip6_conntrack_expect *, &expect->tuple,
|
|
+ &expect->mask);
|
|
+ if (old) {
|
|
+ /* Helper private data may contain offsets but no pointers
|
|
+ pointing into the payload - otherwise we should have to copy
|
|
+ the data filled out by the helper over the old one */
|
|
+ DEBUGP("expect_related: resent packet\n");
|
|
+ if (related_to->helper->timeout) {
|
|
+ if (!del_timer(&old->timeout)) {
|
|
+ /* expectation is dying. Fall through */
|
|
+ old = NULL;
|
|
+ } else {
|
|
+ old->timeout.expires = jiffies +
|
|
+ related_to->helper->timeout * HZ;
|
|
+ add_timer(&old->timeout);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (old) {
|
|
+ WRITE_UNLOCK(&ip6_conntrack_lock);
|
|
+ return -EEXIST;
|
|
+ }
|
|
+ } else if (related_to->helper->max_expected &&
|
|
+ related_to->expecting >= related_to->helper->max_expected) {
|
|
+ struct list_head *cur_item;
|
|
+ /* old == NULL */
|
|
+ if (!(related_to->helper->flags &
|
|
+ IP6_CT_HELPER_F_REUSE_EXPECT)) {
|
|
+ WRITE_UNLOCK(&ip6_conntrack_lock);
|
|
+ if (net_ratelimit())
|
|
+ printk(KERN_WARNING
|
|
+ "ip6_conntrack: max number of expected "
|
|
+ "connections %i of %s for "
|
|
+ "%x:%x:%x:%x:%x:%x:%x:%x->%x:%x:%x:%x:%x:%x:%x:%x\n",
|
|
+ related_to->helper->max_expected,
|
|
+ related_to->helper->name,
|
|
+ NIP6(related_to->tuplehash[IP6_CT_DIR_ORIGINAL].tuple.src.ip),
|
|
+ NIP6(related_to->tuplehash[IP6_CT_DIR_ORIGINAL].tuple.dst.ip));
|
|
+ return -EPERM;
|
|
+ }
|
|
+ DEBUGP("ip6_conntrack: max number of expected "
|
|
+ "connections %i of %s reached for "
|
|
+ "%x:%x:%x:%x:%x:%x:%x:%x->%x:%x:%x:%x:%x:%x:%x:%x, reusing\n",
|
|
+ related_to->helper->max_expected,
|
|
+ related_to->helper->name,
|
|
+ NIP6(related_to->tuplehash[IP6_CT_DIR_ORIGINAL].tuple.src.ip),
|
|
+ NIP6(related_to->tuplehash[IP6_CT_DIR_ORIGINAL].tuple.dst.ip));
|
|
+
|
|
+ /* choose the the oldest expectation to evict */
|
|
+ list_for_each(cur_item, &related_to->sibling_list) {
|
|
+ struct ip6_conntrack_expect *cur;
|
|
+
|
|
+ cur = list_entry(cur_item,
|
|
+ struct ip6_conntrack_expect,
|
|
+ expected_list);
|
|
+ if (cur->sibling == NULL) {
|
|
+ old = cur;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* (!old) cannot happen, since related_to->expecting is the
|
|
+ * number of unconfirmed expects */
|
|
+ IP6_NF_ASSERT(old);
|
|
+
|
|
+ /* newnat14 does not reuse the real allocated memory
|
|
+ * structures but rather unexpects the old and
|
|
+ * allocates a new. unexpect_related will decrement
|
|
+ * related_to->expecting.
|
|
+ */
|
|
+ unexpect_related(old);
|
|
+ ret = -EPERM;
|
|
+ } else if (LIST_FIND(&ip6_conntrack_expect_list, expect_clash,
|
|
+ struct ip6_conntrack_expect *, &expect->tuple,
|
|
+ &expect->mask)) {
|
|
+ WRITE_UNLOCK(&ip6_conntrack_lock);
|
|
+ DEBUGP("expect_related: busy!\n");
|
|
+ return -EBUSY;
|
|
+ }
|
|
+
|
|
+ new = (struct ip6_conntrack_expect *)
|
|
+ kmalloc(sizeof(struct ip6_conntrack_expect), GFP_ATOMIC);
|
|
+ if (!new) {
|
|
+ WRITE_UNLOCK(&ip6_conntrack_lock);
|
|
+ DEBUGP("expect_relaed: OOM allocating expect\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ DEBUGP("new expectation %p of conntrack %p\n", new, related_to);
|
|
+ memcpy(new, expect, sizeof(*expect));
|
|
+ new->expectant = related_to;
|
|
+ new->sibling = NULL;
|
|
+ atomic_set(&new->use, 1);
|
|
+
|
|
+ /* add to expected list for this connection */
|
|
+ list_add(&new->expected_list, &related_to->sibling_list);
|
|
+ /* add to global list of expectations */
|
|
+ list_prepend(&ip6_conntrack_expect_list, &new->list);
|
|
+ /* add and start timer if required */
|
|
+ if (related_to->helper->timeout) {
|
|
+ init_timer(&new->timeout);
|
|
+ new->timeout.data = (unsigned long)new;
|
|
+ new->timeout.function = expectation_timed_out;
|
|
+ new->timeout.expires = jiffies +
|
|
+ related_to->helper->timeout * HZ;
|
|
+ add_timer(&new->timeout);
|
|
+ }
|
|
+ related_to->expecting++;
|
|
+
|
|
+ WRITE_UNLOCK(&ip6_conntrack_lock);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+/* Is this code needed ? this is for NAT. - kozakai */
|
|
+/* Alter reply tuple (maybe alter helper). If it's already taken,
|
|
+ return 0 and don't do alteration. */
|
|
+int ip6_conntrack_alter_reply(struct ip6_conntrack *conntrack,
|
|
+ const struct ip6_conntrack_tuple *newreply)
|
|
+{
|
|
+ WRITE_LOCK(&ip6_conntrack_lock);
|
|
+ if (__ip6_conntrack_find(newreply, conntrack)) {
|
|
+ WRITE_UNLOCK(&ip6_conntrack_lock);
|
|
+ return 0;
|
|
+ }
|
|
+ /* Should be unconfirmed, so not in hash table yet */
|
|
+ IP6_NF_ASSERT(!is_confirmed(conntrack));
|
|
+
|
|
+ DEBUGP("Altering reply tuple of %p to ", conntrack);
|
|
+ DUMP_TUPLE(newreply);
|
|
+
|
|
+ conntrack->tuplehash[IP6_CT_DIR_REPLY].tuple = *newreply;
|
|
+ if (!conntrack->master)
|
|
+ conntrack->helper = ip6_ct_find_helper(newreply);
|
|
+ WRITE_UNLOCK(&ip6_conntrack_lock);
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+int ip6_conntrack_helper_register(struct ip6_conntrack_helper *me)
|
|
+{
|
|
+ WRITE_LOCK(&ip6_conntrack_lock);
|
|
+ list_prepend(&helpers, me);
|
|
+ WRITE_UNLOCK(&ip6_conntrack_lock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static inline int unhelp(struct ip6_conntrack_tuple_hash *i,
|
|
+ const struct ip6_conntrack_helper *me)
|
|
+{
|
|
+ if (i->ctrack->helper == me) {
|
|
+ /* Get rid of any expected. */
|
|
+ remove_expectations(i->ctrack, 0);
|
|
+ /* And *then* set helper to NULL */
|
|
+ i->ctrack->helper = NULL;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void ip6_conntrack_helper_unregister(struct ip6_conntrack_helper *me)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ /* Need write lock here, to delete helper. */
|
|
+ WRITE_LOCK(&ip6_conntrack_lock);
|
|
+ LIST_DELETE(&helpers, me);
|
|
+
|
|
+ /* Get rid of expecteds, set helpers to NULL. */
|
|
+ for (i = 0; i < ip6_conntrack_htable_size; i++)
|
|
+ LIST_FIND_W(&ip6_conntrack_hash[i], unhelp,
|
|
+ struct ip6_conntrack_tuple_hash *, me);
|
|
+ WRITE_UNLOCK(&ip6_conntrack_lock);
|
|
+
|
|
+ /* Someone could be still looking at the helper in a bh. */
|
|
+ synchronize_net();
|
|
+}
|
|
+
|
|
+/* Refresh conntrack for this many jiffies. */
|
|
+void ip6_ct_refresh(struct ip6_conntrack *ct, unsigned long extra_jiffies)
|
|
+{
|
|
+ IP6_NF_ASSERT(ct->timeout.data == (unsigned long)ct);
|
|
+
|
|
+ WRITE_LOCK(&ip6_conntrack_lock);
|
|
+ /* If not in hash table, timer will not be active yet */
|
|
+ if (!is_confirmed(ct))
|
|
+ ct->timeout.expires = extra_jiffies;
|
|
+ else {
|
|
+ /* Need del_timer for race avoidance (may already be dying). */
|
|
+ if (del_timer(&ct->timeout)) {
|
|
+ ct->timeout.expires = jiffies + extra_jiffies;
|
|
+ add_timer(&ct->timeout);
|
|
+ }
|
|
+ }
|
|
+ WRITE_UNLOCK(&ip6_conntrack_lock);
|
|
+}
|
|
+
|
|
+/* Used by ip6t_REJECT. */
|
|
+static void ip6_conntrack_attach(struct sk_buff *nskb, struct sk_buff *skb)
|
|
+{
|
|
+ struct ip6_conntrack *ct;
|
|
+ enum ip6_conntrack_info ctinfo;
|
|
+
|
|
+ ct = ip6_conntrack_get(skb, &ctinfo);
|
|
+
|
|
+ /* This ICMP is in reverse direction to the packet which
|
|
+ caused it */
|
|
+ if (CTINFO2DIR(ctinfo) == IP6_CT_DIR_ORIGINAL)
|
|
+ ctinfo = IP6_CT_RELATED + IP6_CT_IS_REPLY;
|
|
+ else
|
|
+ ctinfo = IP6_CT_RELATED;
|
|
+
|
|
+ /* Attach new skbuff, and increment count */
|
|
+ nskb->nfct = &ct->ct_general;
|
|
+ nskb->nfctinfo = ctinfo;
|
|
+ nf_conntrack_get(nskb->nfct);
|
|
+}
|
|
+
|
|
+static inline int
|
|
+do_kill(const struct ip6_conntrack_tuple_hash *i,
|
|
+ int (*kill)(const struct ip6_conntrack *i, void *data),
|
|
+ void *data)
|
|
+{
|
|
+ return kill(i->ctrack, data);
|
|
+}
|
|
+
|
|
+/* Bring out ya dead! */
|
|
+static struct ip6_conntrack_tuple_hash *
|
|
+get_next_corpse(int (*kill)(const struct ip6_conntrack *i, void *data),
|
|
+ void *data)
|
|
+{
|
|
+ struct ip6_conntrack_tuple_hash *h = NULL;
|
|
+ unsigned int i;
|
|
+
|
|
+ READ_LOCK(&ip6_conntrack_lock);
|
|
+ for (i = 0; !h && i < ip6_conntrack_htable_size; i++) {
|
|
+ h = LIST_FIND(&ip6_conntrack_hash[i], do_kill,
|
|
+ struct ip6_conntrack_tuple_hash *, kill, data);
|
|
+ }
|
|
+ if (h)
|
|
+ atomic_inc(&h->ctrack->ct_general.use);
|
|
+ READ_UNLOCK(&ip6_conntrack_lock);
|
|
+
|
|
+ return h;
|
|
+}
|
|
+
|
|
+void
|
|
+ip6_ct_selective_cleanup(int (*kill)(const struct ip6_conntrack *i, void *data),
|
|
+ void *data)
|
|
+{
|
|
+ struct ip6_conntrack_tuple_hash *h;
|
|
+
|
|
+ /* This is order n^2, by the way. */
|
|
+ while ((h = get_next_corpse(kill, data)) != NULL) {
|
|
+ /* Time to push up daises... */
|
|
+ if (del_timer(&h->ctrack->timeout))
|
|
+ death_by_timeout((unsigned long)h->ctrack);
|
|
+ /* ... else the timer will get him soon. */
|
|
+
|
|
+ ip6_conntrack_put(h->ctrack);
|
|
+ }
|
|
+}
|
|
+
|
|
+/* Fast function for those who don't want to parse /proc (and I don't
|
|
+ blame them). */
|
|
+/* Reversing the socket's dst/src point of view gives us the reply
|
|
+ mapping. */
|
|
+static int
|
|
+getorigdst(struct sock *sk, int optval, void __user *user, int *len)
|
|
+{
|
|
+ struct inet_sock *inet = inet_sk(sk);
|
|
+ struct ipv6_pinfo *np = inet6_sk(sk);
|
|
+ struct ip6_conntrack_tuple_hash *h;
|
|
+ struct ip6_conntrack_tuple tuple;
|
|
+
|
|
+ memset(&tuple, 0, sizeof(tuple));
|
|
+ ipv6_addr_copy(&tuple.src.ip, &np->rcv_saddr);
|
|
+ ipv6_addr_copy(&tuple.dst.ip, &np->daddr);
|
|
+ tuple.src.u.tcp.port = inet->sport;
|
|
+ tuple.dst.u.tcp.port = inet->dport;
|
|
+ tuple.dst.protonum = IPPROTO_TCP;
|
|
+
|
|
+ /* We only do TCP at the moment: is there a better way? */
|
|
+ if (strcmp(sk->sk_prot->name, "TCP")) {
|
|
+ DEBUGP("IPV6_NF_ORIGINAL_DST: Not a TCP socket\n");
|
|
+ return -ENOPROTOOPT;
|
|
+ }
|
|
+
|
|
+ if ((unsigned int) *len < sizeof(struct sockaddr_in6)) {
|
|
+ DEBUGP("IPV6_NF_ORIGINAL_DST: len %u not %u\n",
|
|
+ *len, sizeof(struct sockaddr_in6));
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ h = ip6_conntrack_find_get(&tuple, NULL);
|
|
+ if (h) {
|
|
+ struct sockaddr_in6 sin;
|
|
+ memset(&sin, 0, sizeof(sin));
|
|
+
|
|
+ sin.sin6_family = AF_INET6;
|
|
+ sin.sin6_port = h->ctrack->tuplehash[IP6_CT_DIR_ORIGINAL]
|
|
+ .tuple.dst.u.tcp.port;
|
|
+ ipv6_addr_copy(&sin.sin6_addr,
|
|
+ &h->ctrack->tuplehash[IP6_CT_DIR_ORIGINAL]
|
|
+ .tuple.dst.ip);
|
|
+ /* FIXME: sin6_scope_id */
|
|
+
|
|
+ DEBUGP("IPV6_NF_ORIGINAL_DST: %x:%x:%x:%x:%x:%x:%x:%x %u\n",
|
|
+ NIP6(sin.sin6_addr), ntohs(sin.sin6_port));
|
|
+ ip6_conntrack_put(h->ctrack);
|
|
+ if (copy_to_user(user, &sin, sizeof(sin)) != 0)
|
|
+ return -EFAULT;
|
|
+ else
|
|
+ return 0;
|
|
+ }
|
|
+ DEBUGP("IPV6_NF_ORIGINAL_DST: Can't find %x:%x:%x:%x:%x:%x:%x:%x/%u-%x:%x:%x:%x:%x:%x:%x:%x/%u.\n",
|
|
+ NIP6(tuple.src.ip), ntohs(tuple.src.u.tcp.port),
|
|
+ NIP6(tuple.dst.ip), ntohs(tuple.dst.u.tcp.port));
|
|
+ return -ENOENT;
|
|
+}
|
|
+
|
|
+static struct nf_sockopt_ops so_getorigdst = {
|
|
+ .pf = PF_INET6,
|
|
+ .get_optmin = IPV6_NF_ORIGINAL_DST,
|
|
+ .get_optmax = IPV6_NF_ORIGINAL_DST+1,
|
|
+ .get = &getorigdst,
|
|
+};
|
|
+
|
|
+#define NET_IP6_CONNTRACK_MAX 2089
|
|
+#define NET_IP6_CONNTRACK_MAX_NAME "ip6_conntrack_max"
|
|
+
|
|
+#ifdef CONFIG_SYSCTL
|
|
+static struct ctl_table_header *ip6_conntrack_sysctl_header;
|
|
+
|
|
+static ctl_table ip6_conntrack_table[] = {
|
|
+ {
|
|
+ .ctl_name = NET_IP6_CONNTRACK_MAX,
|
|
+ .procname = NET_IP6_CONNTRACK_MAX_NAME,
|
|
+ .data = &ip6_conntrack_max,
|
|
+ .maxlen = sizeof(ip6_conntrack_max),
|
|
+ .mode = 0644,
|
|
+ .proc_handler = proc_dointvec
|
|
+ },
|
|
+ { .ctl_name = 0 }
|
|
+};
|
|
+
|
|
+static ctl_table ip6_conntrack_dir_table[] = {
|
|
+ {
|
|
+ .ctl_name = NET_IPV6,
|
|
+ .procname = "ipv6", NULL,
|
|
+ .mode = 0555,
|
|
+ .child = ip6_conntrack_table
|
|
+ },
|
|
+ { .ctl_name = 0 }
|
|
+};
|
|
+
|
|
+static ctl_table ip6_conntrack_root_table[] = {
|
|
+ {
|
|
+ .ctl_name = CTL_NET,
|
|
+ .procname = "net",
|
|
+ .mode = 0555,
|
|
+ .child = ip6_conntrack_dir_table
|
|
+ },
|
|
+ { .ctl_name = 0 }
|
|
+};
|
|
+#endif /*CONFIG_SYSCTL*/
|
|
+
|
|
+static int kill_all(const struct ip6_conntrack *i, void *data)
|
|
+{
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/* Mishearing the voices in his head, our hero wonders how he's
|
|
+ supposed to kill the mall. */
|
|
+void ip6_conntrack_cleanup(void)
|
|
+{
|
|
+#ifdef CONFIG_SYSCTL
|
|
+ unregister_sysctl_table(ip6_conntrack_sysctl_header);
|
|
+#endif
|
|
+ ip6_ct_attach = NULL;
|
|
+ /* This makes sure all current packets have passed through
|
|
+ netfilter framework. Roll on, two-stage module
|
|
+ delete... */
|
|
+ synchronize_net();
|
|
+
|
|
+ i_see_dead_people:
|
|
+ ip6_ct_selective_cleanup(kill_all, NULL);
|
|
+ if (atomic_read(&ip6_conntrack_count) != 0) {
|
|
+ schedule();
|
|
+ goto i_see_dead_people;
|
|
+ }
|
|
+
|
|
+ kmem_cache_destroy(ip6_conntrack_cachep);
|
|
+ vfree(ip6_conntrack_hash);
|
|
+ nf_unregister_sockopt(&so_getorigdst);
|
|
+}
|
|
+
|
|
+static int hashsize = 0;
|
|
+MODULE_PARM(hashsize, "i");
|
|
+
|
|
+int __init ip6_conntrack_init(void)
|
|
+{
|
|
+ unsigned int i;
|
|
+ int ret;
|
|
+
|
|
+ /* Idea from tcp.c: use 1/16384 of memory. On i386: 32MB
|
|
+ * machine has 256 buckets. >= 1GB machines have 8192 buckets. */
|
|
+ if (hashsize) {
|
|
+ ip6_conntrack_htable_size = hashsize;
|
|
+ } else {
|
|
+ ip6_conntrack_htable_size
|
|
+ = (((num_physpages << PAGE_SHIFT) / 16384)
|
|
+ / sizeof(struct list_head));
|
|
+ if (num_physpages > (1024 * 1024 * 1024 / PAGE_SIZE))
|
|
+ ip6_conntrack_htable_size = 8192;
|
|
+ if (ip6_conntrack_htable_size < 16)
|
|
+ ip6_conntrack_htable_size = 16;
|
|
+ }
|
|
+ ip6_conntrack_max = 8 * ip6_conntrack_htable_size;
|
|
+
|
|
+ printk("ip6_conntrack version %s (%u buckets, %d max)"
|
|
+ " - %Zd bytes per conntrack\n", IP6_CONNTRACK_VERSION,
|
|
+ ip6_conntrack_htable_size, ip6_conntrack_max,
|
|
+ sizeof(struct ip6_conntrack));
|
|
+
|
|
+ ret = nf_register_sockopt(&so_getorigdst);
|
|
+ if (ret != 0) {
|
|
+ printk(KERN_ERR "Unable to register netfilter socket option\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ip6_conntrack_hash = vmalloc(sizeof(struct list_head)
|
|
+ * ip6_conntrack_htable_size);
|
|
+ if (!ip6_conntrack_hash) {
|
|
+ printk(KERN_ERR "Unable to create ip6_conntrack_hash\n");
|
|
+ goto err_unreg_sockopt;
|
|
+ }
|
|
+
|
|
+ ip6_conntrack_cachep = kmem_cache_create("ip6_conntrack",
|
|
+ sizeof(struct ip6_conntrack), 0,
|
|
+ SLAB_HWCACHE_ALIGN, NULL, NULL);
|
|
+ if (!ip6_conntrack_cachep) {
|
|
+ printk(KERN_ERR "Unable to create ip6_conntrack slab cache\n");
|
|
+ goto err_free_hash;
|
|
+ }
|
|
+ /* Don't NEED lock here, but good form anyway. */
|
|
+ WRITE_LOCK(&ip6_conntrack_lock);
|
|
+ /* Sew in builtin protocols. */
|
|
+ list_append(&ip6_protocol_list, &ip6_conntrack_protocol_tcp);
|
|
+ list_append(&ip6_protocol_list, &ip6_conntrack_protocol_udp);
|
|
+ list_append(&ip6_protocol_list, &ip6_conntrack_protocol_icmpv6);
|
|
+ WRITE_UNLOCK(&ip6_conntrack_lock);
|
|
+
|
|
+ for (i = 0; i < ip6_conntrack_htable_size; i++)
|
|
+ INIT_LIST_HEAD(&ip6_conntrack_hash[i]);
|
|
+
|
|
+/* This is fucking braindead. There is NO WAY of doing this without
|
|
+ the CONFIG_SYSCTL unless you don't want to detect errors.
|
|
+ Grrr... --RR */
|
|
+#ifdef CONFIG_SYSCTL
|
|
+ ip6_conntrack_sysctl_header
|
|
+ = register_sysctl_table(ip6_conntrack_root_table, 0);
|
|
+ if (ip6_conntrack_sysctl_header == NULL) {
|
|
+ goto err_free_ct_cachep;
|
|
+ }
|
|
+#endif /*CONFIG_SYSCTL*/
|
|
+
|
|
+ /* For use by ip6t_REJECT */
|
|
+ ip6_ct_attach = ip6_conntrack_attach;
|
|
+ return ret;
|
|
+
|
|
+#ifdef CONFIG_SYSCTL
|
|
+err_free_ct_cachep:
|
|
+ kmem_cache_destroy(ip6_conntrack_cachep);
|
|
+#endif /*CONFIG_SYSCTL*/
|
|
+err_free_hash:
|
|
+ vfree(ip6_conntrack_hash);
|
|
+err_unreg_sockopt:
|
|
+ nf_unregister_sockopt(&so_getorigdst);
|
|
+
|
|
+ return -ENOMEM;
|
|
+}
|
|
diff -urN linux-2.6.11/net/ipv6/netfilter/ip6_conntrack_ftp.c x1/net/ipv6/netfilter/ip6_conntrack_ftp.c
|
|
--- linux-2.6.11/net/ipv6/netfilter/ip6_conntrack_ftp.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/net/ipv6/netfilter/ip6_conntrack_ftp.c 2003-10-10 07:28:11.000000000 +0200
|
|
@@ -0,0 +1,554 @@
|
|
+/*
|
|
+ * FTP extension for IPv6 connection tracking.
|
|
+ * Linux INET6 implementation
|
|
+ *
|
|
+ * Copyright (C)2003 USAGI/WIDE Project
|
|
+ *
|
|
+ * Authors:
|
|
+ * Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
|
|
+ *
|
|
+ * Based on: net/ipv4/netfilter/ip_conntrack_ftp.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.
|
|
+ */
|
|
+
|
|
+/* FTP extension for IP6 connection tracking. */
|
|
+#include <linux/config.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/netfilter.h>
|
|
+#include <linux/ipv6.h>
|
|
+#include <linux/ctype.h>
|
|
+#include <net/checksum.h>
|
|
+#include <net/tcp.h>
|
|
+#include <net/ipv6.h>
|
|
+#include <linux/kernel.h>
|
|
+
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack.h>
|
|
+#include <linux/netfilter_ipv4/lockhelp.h>
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack_helper.h>
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack_ftp.h>
|
|
+
|
|
+/* This is slow, but it's simple. --RR */
|
|
+static char ftp_buffer[65536];
|
|
+
|
|
+DECLARE_LOCK(ip6_ftp_lock);
|
|
+struct module *ip6_conntrack_ftp = THIS_MODULE;
|
|
+
|
|
+#define MAX_PORTS 8
|
|
+static int ports[MAX_PORTS];
|
|
+static int ports_c = 0;
|
|
+#ifdef MODULE_PARM
|
|
+MODULE_PARM(ports, "1-" __MODULE_STRING(MAX_PORTS) "i");
|
|
+#endif
|
|
+
|
|
+static int loose = 0;
|
|
+MODULE_PARM(loose, "i");
|
|
+
|
|
+#if 0
|
|
+#define DEBUGP printk
|
|
+#else
|
|
+#define DEBUGP(format, args...)
|
|
+#endif
|
|
+
|
|
+struct cmd_info {
|
|
+ struct in6_addr ip;
|
|
+ u_int16_t port;
|
|
+};
|
|
+
|
|
+static int try_eprt(const char *, size_t, struct cmd_info *, char);
|
|
+static int try_espv_response(const char *, size_t, struct cmd_info *, char);
|
|
+
|
|
+static struct ftp_search {
|
|
+ enum ip6_conntrack_dir dir;
|
|
+ const char *pattern;
|
|
+ size_t plen;
|
|
+ char skip;
|
|
+ char term;
|
|
+ enum ip6_ct_ftp_type ftptype;
|
|
+ int (*getnum)(const char *, size_t, struct cmd_info *, char);
|
|
+} search[] = {
|
|
+ {
|
|
+ IP6_CT_DIR_ORIGINAL,
|
|
+ "EPRT", sizeof("EPRT") - 1, ' ', '\r',
|
|
+ IP6_CT_FTP_EPRT,
|
|
+ try_eprt,
|
|
+ },
|
|
+ {
|
|
+ IP6_CT_DIR_REPLY,
|
|
+ "229 ", sizeof("229 ") - 1, '(', ')',
|
|
+ IP6_CT_FTP_EPSV,
|
|
+ try_espv_response,
|
|
+ },
|
|
+};
|
|
+
|
|
+/* This code is based on inet_pton() in glibc-2.2.4 */
|
|
+
|
|
+#define NS_IN6ADDRSZ 16
|
|
+#define NS_INADDRSZ 4
|
|
+#define NS_INT16SZ 2
|
|
+
|
|
+/*
|
|
+ * return the length of string of address parse untill error,
|
|
+ * dlen or reaching terminal char - kozakai
|
|
+ */
|
|
+static int
|
|
+get_ipv6_addr(const char *src, u_int8_t *dst, size_t dlen, u_int8_t term)
|
|
+{
|
|
+ static const char xdigits[] = "0123456789abcdef";
|
|
+ u_int8_t tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
|
|
+ const char *curtok;
|
|
+ int ch, saw_xdigit;
|
|
+ u_int32_t val;
|
|
+ size_t clen = 0;
|
|
+
|
|
+ tp = memset(tmp, '\0', NS_IN6ADDRSZ);
|
|
+ endp = tp + NS_IN6ADDRSZ;
|
|
+ colonp = NULL;
|
|
+
|
|
+ /* Leading :: requires some special handling. */
|
|
+ if (*src == ':'){
|
|
+ if (*++src != ':')
|
|
+ return (0);
|
|
+ clen++;
|
|
+ }
|
|
+
|
|
+ curtok = src;
|
|
+ saw_xdigit = 0;
|
|
+ val = 0;
|
|
+ while ((clen < dlen) && (*src != term)) {
|
|
+ const char *pch;
|
|
+
|
|
+ ch = tolower (*src++);
|
|
+ clen++;
|
|
+
|
|
+ pch = strchr(xdigits, ch);
|
|
+ if (pch != NULL) {
|
|
+ val <<= 4;
|
|
+ val |= (pch - xdigits);
|
|
+ if (val > 0xffff)
|
|
+ return (0);
|
|
+
|
|
+ saw_xdigit = 1;
|
|
+ continue;
|
|
+ }
|
|
+ if (ch == ':') {
|
|
+ curtok = src;
|
|
+ if (!saw_xdigit) {
|
|
+ if (colonp)
|
|
+ return (0);
|
|
+ colonp = tp;
|
|
+ continue;
|
|
+ } else if (*src == term) {
|
|
+ return (0);
|
|
+ }
|
|
+ if (tp + NS_INT16SZ > endp)
|
|
+ return (0);
|
|
+ *tp++ = (u_int8_t) (val >> 8) & 0xff;
|
|
+ *tp++ = (u_int8_t) val & 0xff;
|
|
+ saw_xdigit = 0;
|
|
+ val = 0;
|
|
+ continue;
|
|
+ }
|
|
+ return (0);
|
|
+ }
|
|
+ if (saw_xdigit) {
|
|
+ if (tp + NS_INT16SZ > endp)
|
|
+ return (0);
|
|
+
|
|
+ *tp++ = (u_int8_t) (val >> 8) & 0xff;
|
|
+ *tp++ = (u_int8_t) val & 0xff;
|
|
+ }
|
|
+ if (colonp != NULL) {
|
|
+ /*
|
|
+ * Since some memmove()'s erroneously fail to handle
|
|
+ * overlapping regions, we'll do the shift by hand.
|
|
+ */
|
|
+ const int n = tp - colonp;
|
|
+ int i;
|
|
+
|
|
+ if (tp == endp)
|
|
+ return (0);
|
|
+
|
|
+ for (i = 1; i <= n; i++) {
|
|
+ endp[- i] = colonp[n - i];
|
|
+ colonp[n - i] = 0;
|
|
+ }
|
|
+ tp = endp;
|
|
+ }
|
|
+ if (tp != endp || (*src != term))
|
|
+ return (0);
|
|
+
|
|
+ memcpy(dst, tmp, NS_IN6ADDRSZ);
|
|
+ return clen;
|
|
+}
|
|
+
|
|
+/* return length of port if succeed. */
|
|
+static int get_port(const char *data, u_int16_t *port, size_t dlen, char term)
|
|
+{
|
|
+ int i;
|
|
+ u_int16_t tmp_port = 0;
|
|
+
|
|
+ for(i = 0; i < dlen; i++) {
|
|
+ /* Finished? */
|
|
+ if(data[i] == term){
|
|
+ *port = htons(tmp_port);
|
|
+ return i;
|
|
+ }
|
|
+
|
|
+ if(data[i] < '0' || data[i] > '9')
|
|
+ return 0;
|
|
+
|
|
+ tmp_port = tmp_port*10 + (data[i] - '0');
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Returns 0, or length of numbers: |1|132.235.1.2|6275| */
|
|
+static int try_eprt(const char *data, size_t dlen, struct cmd_info *cmd,
|
|
+ char term)
|
|
+{
|
|
+ char delim;
|
|
+ int len;
|
|
+ int addr_len;
|
|
+
|
|
+ /* First character is delimiter, then "1" for IPv4, then
|
|
+ delimiter again. */
|
|
+
|
|
+ if (dlen <= 3)
|
|
+ return 0;
|
|
+
|
|
+ delim = data[0];
|
|
+
|
|
+ if (isdigit(delim) || delim < 33 || delim > 126
|
|
+ || data[1] != '2' || data[2] != delim){
|
|
+ return 0;
|
|
+ }
|
|
+ DEBUGP("Got %c2%c\n", delim, delim);
|
|
+
|
|
+ len = 3;
|
|
+
|
|
+ /* Now we have IP address. */
|
|
+ addr_len = get_ipv6_addr(&data[len], cmd->ip.s6_addr,
|
|
+ dlen - len, delim);
|
|
+
|
|
+ if (addr_len == 0)
|
|
+ return 0;
|
|
+
|
|
+ len += addr_len + 1;
|
|
+
|
|
+ DEBUGP("Got IPv6 address!\n");
|
|
+
|
|
+ addr_len = get_port(&data[len], &cmd->port, dlen, delim);
|
|
+
|
|
+ if(addr_len == 0)
|
|
+ return 0;
|
|
+
|
|
+ len += addr_len + 1;
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+/* Returns 0, or length of numbers: |||6446| */
|
|
+static int try_espv_response(const char *data, size_t dlen,
|
|
+ struct cmd_info *cmd, char term)
|
|
+{
|
|
+ char delim;
|
|
+ size_t len;
|
|
+
|
|
+ /* Three delimiters. */
|
|
+ if (dlen <= 3)
|
|
+ return 0;
|
|
+
|
|
+ delim = data[0];
|
|
+
|
|
+ if (isdigit(delim) || delim < 33 || delim > 126
|
|
+ || data[1] != delim || data[2] != delim)
|
|
+ return 0;
|
|
+
|
|
+ len = get_port(&data[3], &cmd->port, dlen, delim);
|
|
+
|
|
+ if(len == 0)
|
|
+ return 0;
|
|
+
|
|
+ return 3 + len + 1;
|
|
+}
|
|
+
|
|
+/* Return 1 for match, 0 for accept, -1 for partial. */
|
|
+static int find_pattern(const char *data, size_t dlen,
|
|
+ const char *pattern, size_t plen,
|
|
+ char skip, char term,
|
|
+ unsigned int *numoff,
|
|
+ unsigned int *numlen,
|
|
+ struct cmd_info *cmd,
|
|
+ int (*getnum)(const char *, size_t, struct cmd_info *,
|
|
+ char))
|
|
+{
|
|
+ size_t i;
|
|
+
|
|
+ DEBUGP("find_pattern `%s': dlen = %u\n", pattern, dlen);
|
|
+ if (dlen == 0)
|
|
+ return 0;
|
|
+
|
|
+ if (dlen <= plen) {
|
|
+ /* Short packet: try for partial? */
|
|
+ if (strnicmp(data, pattern, dlen) == 0)
|
|
+ return -1;
|
|
+ else return 0;
|
|
+ }
|
|
+
|
|
+ if (strnicmp(data, pattern, plen) != 0) {
|
|
+#if 0
|
|
+ size_t i;
|
|
+
|
|
+ DEBUGP("ftp: string mismatch\n");
|
|
+ for (i = 0; i < plen; i++) {
|
|
+ DEBUGP("ftp:char %u `%c'(%u) vs `%c'(%u)\n",
|
|
+ i, data[i], data[i],
|
|
+ pattern[i], pattern[i]);
|
|
+ }
|
|
+#endif
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ DEBUGP("Pattern matches!\n");
|
|
+ /* Now we've found the constant string, try to skip
|
|
+ to the 'skip' character */
|
|
+ for (i = plen; data[i] != skip; i++)
|
|
+ if (i == dlen - 1) return -1;
|
|
+
|
|
+ /* Skip over the last character */
|
|
+ i++;
|
|
+
|
|
+ DEBUGP("Skipped up to `%c'!\n", skip);
|
|
+
|
|
+ *numoff = i;
|
|
+ *numlen = getnum(data + i, dlen - i, cmd, term);
|
|
+ if (!*numlen)
|
|
+ return -1;
|
|
+
|
|
+ DEBUGP("Match succeeded!\n");
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int help(const struct sk_buff *skb,
|
|
+ unsigned int protoff,
|
|
+ struct ip6_conntrack *ct,
|
|
+ enum ip6_conntrack_info ctinfo)
|
|
+{
|
|
+ unsigned int dataoff, datalen;
|
|
+ struct tcphdr tcph;
|
|
+ u_int32_t old_seq_aft_nl;
|
|
+ int old_seq_aft_nl_set, ret;
|
|
+ int dir = CTINFO2DIR(ctinfo);
|
|
+ unsigned int matchlen, matchoff;
|
|
+ struct ip6_ct_ftp_master *ct_ftp_info = &ct->help.ct_ftp_info;
|
|
+ struct ip6_conntrack_expect expect, *exp = &expect;
|
|
+ struct ip6_ct_ftp_expect *exp_ftp_info = &exp->help.exp_ftp_info;
|
|
+
|
|
+ unsigned int i;
|
|
+ int found = 0;
|
|
+
|
|
+ struct ipv6hdr *ipv6h = skb->nh.ipv6h;
|
|
+ struct ip6_conntrack_tuple *t = &exp->tuple, *mask = &exp->mask;
|
|
+ struct cmd_info cmd;
|
|
+ unsigned int csum;
|
|
+
|
|
+ /* Until there's been traffic both ways, don't look in packets. */
|
|
+ if (ctinfo != IP6_CT_ESTABLISHED
|
|
+ && ctinfo != IP6_CT_ESTABLISHED+IP6_CT_IS_REPLY) {
|
|
+ DEBUGP("ftp: Conntrackinfo = %u\n", ctinfo);
|
|
+ return NF_ACCEPT;
|
|
+ }
|
|
+
|
|
+ if (skb_copy_bits(skb, protoff, &tcph, sizeof(tcph)) != 0)
|
|
+ return NF_ACCEPT;
|
|
+
|
|
+ dataoff = protoff + tcph.doff * 4;
|
|
+ /* No data? */
|
|
+ if (dataoff >= skb->len) {
|
|
+ DEBUGP("ftp: dataoff(%u) >= skblen(%u)\n", dataoff, skb->len);
|
|
+ return NF_ACCEPT;
|
|
+ }
|
|
+ datalen = skb->len - dataoff;
|
|
+
|
|
+ LOCK_BH(&ip6_ftp_lock);
|
|
+
|
|
+ csum = skb_copy_and_csum_bits(skb, dataoff, ftp_buffer,
|
|
+ skb->len - dataoff, 0);
|
|
+ csum = skb_checksum(skb, protoff, tcph.doff * 4, csum);
|
|
+
|
|
+ /* Checksum invalid? Ignore. */
|
|
+ /* FIXME: Source route IP option packets --RR */
|
|
+ if (csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len - protoff,
|
|
+ IPPROTO_TCP, csum)) {
|
|
+ DEBUGP("ftp_help: bad csum: %p %u\n"
|
|
+ "%x:%x:%x:%x:%x:%x:%x:%x -> %x:%x:%x:%x:%x:%x:%x:%x\n",
|
|
+ &tcph, skb->len - protoff, NIP6(ipv6h->saddr),
|
|
+ NIP6(ipv6h->daddr));
|
|
+ ret = NF_ACCEPT;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ old_seq_aft_nl_set = ct_ftp_info->seq_aft_nl_set[dir];
|
|
+ old_seq_aft_nl = ct_ftp_info->seq_aft_nl[dir];
|
|
+
|
|
+ DEBUGP("conntrack_ftp: datalen %u\n", datalen);
|
|
+ if (ftp_buffer[datalen - 1] == '\n') {
|
|
+ DEBUGP("conntrack_ftp: datalen %u ends in \\n\n", datalen);
|
|
+ if (!old_seq_aft_nl_set
|
|
+ || after(ntohl(tcph.seq) + datalen, old_seq_aft_nl)) {
|
|
+ DEBUGP("conntrack_ftp: updating nl to %u\n",
|
|
+ ntohl(tcph.seq) + datalen);
|
|
+ ct_ftp_info->seq_aft_nl[dir] =
|
|
+ ntohl(tcph.seq) + datalen;
|
|
+ ct_ftp_info->seq_aft_nl_set[dir] = 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if(!old_seq_aft_nl_set ||
|
|
+ (ntohl(tcph.seq) != old_seq_aft_nl)) {
|
|
+ DEBUGP("ip6_conntrack_ftp_help: wrong seq pos %s(%u)\n",
|
|
+ old_seq_aft_nl_set ? "":"(UNSET) ", old_seq_aft_nl);
|
|
+ ret = NF_ACCEPT;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* Initialize IP array to expected address (it's not mentioned
|
|
+ in EPSV responses) */
|
|
+ ipv6_addr_copy(&cmd.ip, &ct->tuplehash[dir].tuple.src.ip);
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(search); i++) {
|
|
+ if (search[i].dir != dir) continue;
|
|
+
|
|
+ found = find_pattern(ftp_buffer, datalen,
|
|
+ search[i].pattern,
|
|
+ search[i].plen,
|
|
+ search[i].skip,
|
|
+ search[i].term,
|
|
+ &matchoff, &matchlen,
|
|
+ &cmd,
|
|
+ search[i].getnum);
|
|
+ if (found) break;
|
|
+ }
|
|
+ if (found == -1) {
|
|
+ /* We don't usually drop packets. After all, this is
|
|
+ connection tracking, not packet filtering.
|
|
+ However, it is neccessary for accurate tracking in
|
|
+ this case. */
|
|
+ if (net_ratelimit())
|
|
+ printk("conntrack_ftp: partial %s %u+%u\n",
|
|
+ search[i].pattern,
|
|
+ ntohl(tcph.seq), datalen);
|
|
+ ret = NF_DROP;
|
|
+ goto out;
|
|
+ } else if (found == 0) { /* No match */
|
|
+ ret = NF_ACCEPT;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ DEBUGP("conntrack_ftp: match `%.*s' (%u bytes at %u)\n",
|
|
+ (int)matchlen, ftp_buffer + matchoff,
|
|
+ matchlen, ntohl(tcph.seq) + matchoff);
|
|
+
|
|
+ memset(&expect, 0, sizeof(expect));
|
|
+
|
|
+ /* Update the ftp info */
|
|
+ if (!ipv6_addr_cmp(&cmd.ip, &ct->tuplehash[dir].tuple.src.ip)) {
|
|
+ exp->seq = ntohl(tcph.seq) + matchoff;
|
|
+ exp_ftp_info->len = matchlen;
|
|
+ exp_ftp_info->ftptype = search[i].ftptype;
|
|
+ exp_ftp_info->port = cmd.port;
|
|
+ } else {
|
|
+ /*
|
|
+ This situation is occurred with NAT.
|
|
+ */
|
|
+ if (!loose) {
|
|
+ ret = NF_ACCEPT;
|
|
+ goto out;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ipv6_addr_copy(&t->src.ip, &ct->tuplehash[!dir].tuple.src.ip);
|
|
+ ipv6_addr_copy(&t->dst.ip, &cmd.ip);
|
|
+ t->src.u.tcp.port = 0;
|
|
+ t->dst.u.tcp.port = cmd.port;
|
|
+ t->dst.protonum = IPPROTO_TCP;
|
|
+
|
|
+ ipv6_addr_set(&mask->src.ip, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF);
|
|
+ mask->src.u.tcp.port = 0;
|
|
+ mask->dst.u.tcp.port = 0xFFFF;
|
|
+ mask->dst.protonum = 0xFFFF;
|
|
+
|
|
+ exp->expectfn = NULL;
|
|
+
|
|
+ /* Ignore failure; should only happen with NAT */
|
|
+ ip6_conntrack_expect_related(ct, &expect);
|
|
+ ret = NF_ACCEPT;
|
|
+ out:
|
|
+ UNLOCK_BH(&ip6_ftp_lock);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static struct ip6_conntrack_helper ftp[MAX_PORTS];
|
|
+static char ftp_names[MAX_PORTS][10];
|
|
+
|
|
+/* Not __exit: called from init() */
|
|
+static void fini(void)
|
|
+{
|
|
+ int i;
|
|
+ for (i = 0; i < ports_c; i++) {
|
|
+ DEBUGP("ip6_ct_ftp: unregistering helper for port %d\n",
|
|
+ ports[i]);
|
|
+ ip6_conntrack_helper_unregister(&ftp[i]);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int __init init(void)
|
|
+{
|
|
+ int i, ret;
|
|
+ char *tmpname;
|
|
+
|
|
+ if (ports[0] == 0)
|
|
+ ports[0] = FTP_PORT;
|
|
+
|
|
+ for (i = 0; (i < MAX_PORTS) && ports[i]; i++) {
|
|
+ memset(&ftp[i], 0, sizeof(struct ip6_conntrack_helper));
|
|
+ ftp[i].tuple.src.u.tcp.port = htons(ports[i]);
|
|
+ ftp[i].tuple.dst.protonum = IPPROTO_TCP;
|
|
+ ftp[i].mask.src.u.tcp.port = 0xFFFF;
|
|
+ ftp[i].mask.dst.protonum = 0xFFFF;
|
|
+ ftp[i].max_expected = 1;
|
|
+ ftp[i].timeout = 0;
|
|
+ ftp[i].flags = IP6_CT_HELPER_F_REUSE_EXPECT;
|
|
+ ftp[i].me = ip6_conntrack_ftp;
|
|
+ ftp[i].help = help;
|
|
+
|
|
+ tmpname = &ftp_names[i][0];
|
|
+ if (ports[i] == FTP_PORT)
|
|
+ sprintf(tmpname, "ftp");
|
|
+ else
|
|
+ sprintf(tmpname, "ftp-%d", ports[i]);
|
|
+ ftp[i].name = tmpname;
|
|
+
|
|
+ DEBUGP("ip6_ct_ftp: registering helper for port %d\n",
|
|
+ ports[i]);
|
|
+ ret = ip6_conntrack_helper_register(&ftp[i]);
|
|
+
|
|
+ if (ret) {
|
|
+ fini();
|
|
+ return ret;
|
|
+ }
|
|
+ ports_c++;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+PROVIDES_CONNTRACK6(ftp);
|
|
+EXPORT_SYMBOL(ip6_ftp_lock);
|
|
+MODULE_LICENSE("GPL");
|
|
+module_init(init);
|
|
+module_exit(fini);
|
|
diff -urN linux-2.6.11/net/ipv6/netfilter/ip6_conntrack_proto_generic.c x1/net/ipv6/netfilter/ip6_conntrack_proto_generic.c
|
|
--- linux-2.6.11/net/ipv6/netfilter/ip6_conntrack_proto_generic.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/net/ipv6/netfilter/ip6_conntrack_proto_generic.c 2003-09-18 12:48:46.000000000 +0200
|
|
@@ -0,0 +1,82 @@
|
|
+/*
|
|
+ * IPv6 generic protocol extension for IPv6 connection tracking
|
|
+ * Linux INET6 implementation
|
|
+ *
|
|
+ * Copyright (C)2003 USAGI/WIDE Project
|
|
+ *
|
|
+ * Authors:
|
|
+ * Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
|
|
+ *
|
|
+ * Based on: net/ipv4/netfilter/ip_conntrack_proto_generic.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.
|
|
+ */
|
|
+
|
|
+#include <linux/types.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/timer.h>
|
|
+#include <linux/netfilter.h>
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack_protocol.h>
|
|
+
|
|
+#define GENERIC_TIMEOUT (600*HZ)
|
|
+
|
|
+static int generic_pkt_to_tuple(const struct sk_buff *skb,
|
|
+ unsigned int dataoff,
|
|
+ struct ip6_conntrack_tuple *tuple)
|
|
+{
|
|
+ tuple->src.u.all = 0;
|
|
+ tuple->dst.u.all = 0;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int generic_invert_tuple(struct ip6_conntrack_tuple *tuple,
|
|
+ const struct ip6_conntrack_tuple *orig)
|
|
+{
|
|
+ tuple->src.u.all = 0;
|
|
+ tuple->dst.u.all = 0;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/* Print out the per-protocol part of the tuple. */
|
|
+static unsigned int generic_print_tuple(char *buffer,
|
|
+ const struct ip6_conntrack_tuple *tuple)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Print out the private part of the conntrack. */
|
|
+static unsigned int generic_print_conntrack(char *buffer,
|
|
+ const struct ip6_conntrack *state)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Returns verdict for packet, or -1 for invalid. */
|
|
+static int established(struct ip6_conntrack *conntrack,
|
|
+ const struct sk_buff *skb,
|
|
+ unsigned int dataoff,
|
|
+ enum ip6_conntrack_info conntrackinfo)
|
|
+{
|
|
+ ip6_ct_refresh(conntrack, GENERIC_TIMEOUT);
|
|
+ return NF_ACCEPT;
|
|
+}
|
|
+
|
|
+/* Called when a new connection for this protocol found. */
|
|
+static int
|
|
+new(struct ip6_conntrack *conntrack,
|
|
+ const struct sk_buff *skb,
|
|
+ unsigned int dataoff)
|
|
+{
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+struct ip6_conntrack_protocol ip6_conntrack_generic_protocol
|
|
+= { { NULL, NULL }, 0, "unknown",
|
|
+ generic_pkt_to_tuple, generic_invert_tuple, generic_print_tuple,
|
|
+ generic_print_conntrack, established, new, NULL, NULL, NULL };
|
|
+
|
|
diff -urN linux-2.6.11/net/ipv6/netfilter/ip6_conntrack_proto_icmpv6.c x1/net/ipv6/netfilter/ip6_conntrack_proto_icmpv6.c
|
|
--- linux-2.6.11/net/ipv6/netfilter/ip6_conntrack_proto_icmpv6.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/net/ipv6/netfilter/ip6_conntrack_proto_icmpv6.c 2003-09-18 12:48:46.000000000 +0200
|
|
@@ -0,0 +1,135 @@
|
|
+/*
|
|
+ * ICMPv6 extension for IPv6 connection tracking
|
|
+ * Linux INET6 implementation
|
|
+ *
|
|
+ * Copyright (C)2003 USAGI/WIDE Project
|
|
+ *
|
|
+ * Authors:
|
|
+ * Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
|
|
+ *
|
|
+ * Based on: net/ipv4/netfilter/ip_conntrack_proto_icmp.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.
|
|
+ */
|
|
+
|
|
+#include <linux/types.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/timer.h>
|
|
+#include <linux/netfilter.h>
|
|
+#include <linux/in.h>
|
|
+#include <linux/icmpv6.h>
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack_protocol.h>
|
|
+
|
|
+#define ICMPV6_TIMEOUT (30*HZ)
|
|
+
|
|
+#if 0
|
|
+#define DEBUGP printk
|
|
+#else
|
|
+#define DEBUGP(format, args...)
|
|
+#endif
|
|
+
|
|
+static int icmpv6_pkt_to_tuple(const struct sk_buff *skb,
|
|
+ unsigned int dataoff,
|
|
+ struct ip6_conntrack_tuple *tuple)
|
|
+{
|
|
+ struct icmp6hdr hdr;
|
|
+
|
|
+ if (skb_copy_bits(skb, dataoff, &hdr, sizeof(hdr)) != 0)
|
|
+ return 0;
|
|
+ tuple->dst.u.icmpv6.type = hdr.icmp6_type;
|
|
+ tuple->src.u.icmpv6.id = hdr.icmp6_identifier;
|
|
+ tuple->dst.u.icmpv6.code = hdr.icmp6_code;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int icmpv6_invert_tuple(struct ip6_conntrack_tuple *tuple,
|
|
+ const struct ip6_conntrack_tuple *orig)
|
|
+{
|
|
+ /* Add 1; spaces filled with 0. */
|
|
+ static u_int8_t invmap[] = {
|
|
+ [ICMPV6_ECHO_REQUEST] = ICMPV6_ECHO_REPLY + 1,
|
|
+ [ICMPV6_ECHO_REPLY] = ICMPV6_ECHO_REQUEST + 1,
|
|
+ [ICMPV6_NI_QUERY] = ICMPV6_NI_QUERY + 1,
|
|
+ [ICMPV6_NI_REPLY] = ICMPV6_NI_REPLY +1
|
|
+ };
|
|
+
|
|
+ if (orig->dst.u.icmpv6.type >= sizeof(invmap)
|
|
+ || !invmap[orig->dst.u.icmpv6.type])
|
|
+ return 0;
|
|
+
|
|
+ tuple->src.u.icmpv6.id = orig->src.u.icmpv6.id;
|
|
+ tuple->dst.u.icmpv6.type = invmap[orig->dst.u.icmpv6.type] - 1;
|
|
+ tuple->dst.u.icmpv6.code = orig->dst.u.icmpv6.code;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/* Print out the per-protocol part of the tuple. */
|
|
+static unsigned int icmpv6_print_tuple(char *buffer,
|
|
+ const struct ip6_conntrack_tuple *tuple)
|
|
+{
|
|
+ return sprintf(buffer, "type=%u code=%u id=%u ",
|
|
+ tuple->dst.u.icmpv6.type,
|
|
+ tuple->dst.u.icmpv6.code,
|
|
+ ntohs(tuple->src.u.icmpv6.id));
|
|
+}
|
|
+
|
|
+/* Print out the private part of the conntrack. */
|
|
+static unsigned int icmpv6_print_conntrack(char *buffer,
|
|
+ const struct ip6_conntrack *conntrack)
|
|
+{
|
|
+ return sprintf(buffer, "count=%u ",
|
|
+ atomic_read(&conntrack->proto.icmpv6.count));
|
|
+}
|
|
+
|
|
+/* Returns verdict for packet, or -1 for invalid. */
|
|
+static int icmpv6_packet(struct ip6_conntrack *ct,
|
|
+ const struct sk_buff *skb,
|
|
+ unsigned int dataoff,
|
|
+ enum ip6_conntrack_info ctinfo)
|
|
+{
|
|
+ /* Try to delete connection immediately after all replies:
|
|
+ won't actually vanish as we still have skb, and del_timer
|
|
+ means this will only run once even if count hits zero twice
|
|
+ (theoretically possible with SMP) */
|
|
+ if (CTINFO2DIR(ctinfo) == IP6_CT_DIR_REPLY) {
|
|
+ if (atomic_dec_and_test(&ct->proto.icmpv6.count)
|
|
+ && del_timer(&ct->timeout))
|
|
+ ct->timeout.function((unsigned long)ct);
|
|
+ } else {
|
|
+ atomic_inc(&ct->proto.icmpv6.count);
|
|
+ ip6_ct_refresh(ct, ICMPV6_TIMEOUT);
|
|
+ }
|
|
+
|
|
+ return NF_ACCEPT;
|
|
+}
|
|
+
|
|
+/* Called when a new connection for this protocol found. */
|
|
+static int icmpv6_new(struct ip6_conntrack *conntrack,
|
|
+ const struct sk_buff *skb,
|
|
+ unsigned int dataoff)
|
|
+{
|
|
+ static u_int8_t valid_new[] = {
|
|
+ [ICMPV6_ECHO_REQUEST] = 1,
|
|
+ [ICMPV6_NI_QUERY] = 1
|
|
+ };
|
|
+
|
|
+ if (conntrack->tuplehash[0].tuple.dst.u.icmpv6.type >= sizeof(valid_new)
|
|
+ || !valid_new[conntrack->tuplehash[0].tuple.dst.u.icmpv6.type]) {
|
|
+ /* Can't create a new ICMPV6 `conn' with this. */
|
|
+ DEBUGP("icmpv6: can't create new conn with type %u\n",
|
|
+ conntrack->tuplehash[0].tuple.dst.u.icmpv6.type);
|
|
+ DUMP_TUPLE(&conntrack->tuplehash[0].tuple);
|
|
+ return 0;
|
|
+ }
|
|
+ atomic_set(&conntrack->proto.icmpv6.count, 0);
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+struct ip6_conntrack_protocol ip6_conntrack_protocol_icmpv6
|
|
+= { { NULL, NULL }, IPPROTO_ICMPV6, "icmpv6",
|
|
+ icmpv6_pkt_to_tuple, icmpv6_invert_tuple, icmpv6_print_tuple,
|
|
+ icmpv6_print_conntrack, icmpv6_packet, icmpv6_new, NULL, NULL, NULL };
|
|
diff -urN linux-2.6.11/net/ipv6/netfilter/ip6_conntrack_proto_tcp.c x1/net/ipv6/netfilter/ip6_conntrack_proto_tcp.c
|
|
--- linux-2.6.11/net/ipv6/netfilter/ip6_conntrack_proto_tcp.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/net/ipv6/netfilter/ip6_conntrack_proto_tcp.c 2003-09-18 12:48:46.000000000 +0200
|
|
@@ -0,0 +1,273 @@
|
|
+/*
|
|
+ * TCP extension for IPv6 Connection Tracking
|
|
+ * Linux INET6 implementation
|
|
+ *
|
|
+ * Copyright (C)2003 USAGI/WIDE Project
|
|
+ *
|
|
+ * Authors:
|
|
+ * Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
|
|
+ *
|
|
+ * Based on: net/ipv4/netfilter/ip_conntrack_proto_tcp.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.
|
|
+ */
|
|
+
|
|
+#include <linux/types.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/timer.h>
|
|
+#include <linux/netfilter.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/in.h>
|
|
+#include <linux/ipv6.h>
|
|
+#include <linux/tcp.h>
|
|
+#include <linux/string.h>
|
|
+
|
|
+#include <net/tcp.h>
|
|
+
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack.h>
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack_protocol.h>
|
|
+#include <linux/netfilter_ipv4/lockhelp.h>
|
|
+
|
|
+#if 0
|
|
+#define DEBUGP printk
|
|
+#else
|
|
+#define DEBUGP(format, args...)
|
|
+#endif
|
|
+
|
|
+/* Protects conntrack->proto.tcp */
|
|
+static DECLARE_RWLOCK(tcp_lock);
|
|
+
|
|
+/* FIXME: Examine ipfilter's timeouts and conntrack transitions more
|
|
+ closely. They're more complex. --RR */
|
|
+
|
|
+/* Actually, I believe that neither ipmasq (where this code is stolen
|
|
+ from) nor ipfilter do it exactly right. A new conntrack machine taking
|
|
+ into account packet loss (which creates uncertainty as to exactly
|
|
+ the conntrack of the connection) is required. RSN. --RR */
|
|
+
|
|
+static const char *tcp_conntrack_names[] = {
|
|
+ "NONE",
|
|
+ "ESTABLISHED",
|
|
+ "SYN_SENT",
|
|
+ "SYN_RECV",
|
|
+ "FIN_WAIT",
|
|
+ "TIME_WAIT",
|
|
+ "CLOSE",
|
|
+ "CLOSE_WAIT",
|
|
+ "LAST_ACK",
|
|
+ "LISTEN"
|
|
+};
|
|
+
|
|
+#define SECS *HZ
|
|
+#define MINS * 60 SECS
|
|
+#define HOURS * 60 MINS
|
|
+#define DAYS * 24 HOURS
|
|
+
|
|
+
|
|
+static unsigned long tcp_timeouts[]
|
|
+= { 30 MINS, /* TCP_CONNTRACK_NONE, */
|
|
+ 5 DAYS, /* TCP_CONNTRACK_ESTABLISHED, */
|
|
+ 2 MINS, /* TCP_CONNTRACK_SYN_SENT, */
|
|
+ 60 SECS, /* TCP_CONNTRACK_SYN_RECV, */
|
|
+ 2 MINS, /* TCP_CONNTRACK_FIN_WAIT, */
|
|
+ 2 MINS, /* TCP_CONNTRACK_TIME_WAIT, */
|
|
+ 10 SECS, /* TCP_CONNTRACK_CLOSE, */
|
|
+ 60 SECS, /* TCP_CONNTRACK_CLOSE_WAIT, */
|
|
+ 30 SECS, /* TCP_CONNTRACK_LAST_ACK, */
|
|
+ 2 MINS, /* TCP_CONNTRACK_LISTEN, */
|
|
+};
|
|
+
|
|
+#define sNO TCP_CONNTRACK_NONE
|
|
+#define sES TCP_CONNTRACK_ESTABLISHED
|
|
+#define sSS TCP_CONNTRACK_SYN_SENT
|
|
+#define sSR TCP_CONNTRACK_SYN_RECV
|
|
+#define sFW TCP_CONNTRACK_FIN_WAIT
|
|
+#define sTW TCP_CONNTRACK_TIME_WAIT
|
|
+#define sCL TCP_CONNTRACK_CLOSE
|
|
+#define sCW TCP_CONNTRACK_CLOSE_WAIT
|
|
+#define sLA TCP_CONNTRACK_LAST_ACK
|
|
+#define sLI TCP_CONNTRACK_LISTEN
|
|
+#define sIV TCP_CONNTRACK_MAX
|
|
+
|
|
+static enum tcp_conntrack tcp_conntracks[2][5][TCP_CONNTRACK_MAX] = {
|
|
+ {
|
|
+/* ORIGINAL */
|
|
+/* sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI */
|
|
+/*syn*/ {sSS, sES, sSS, sSR, sSS, sSS, sSS, sSS, sSS, sLI },
|
|
+/*fin*/ {sTW, sFW, sSS, sTW, sFW, sTW, sCL, sTW, sLA, sLI },
|
|
+/*ack*/ {sES, sES, sSS, sES, sFW, sTW, sCL, sCW, sLA, sES },
|
|
+/*rst*/ {sCL, sCL, sSS, sCL, sCL, sTW, sCL, sCL, sCL, sCL },
|
|
+/*none*/{sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
|
|
+ },
|
|
+ {
|
|
+/* REPLY */
|
|
+/* sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI */
|
|
+/*syn*/ {sSR, sES, sSR, sSR, sSR, sSR, sSR, sSR, sSR, sSR },
|
|
+/*fin*/ {sCL, sCW, sSS, sTW, sTW, sTW, sCL, sCW, sLA, sLI },
|
|
+/*ack*/ {sCL, sES, sSS, sSR, sFW, sTW, sCL, sCW, sCL, sLI },
|
|
+/*rst*/ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sLA, sLI },
|
|
+/*none*/{sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
|
|
+ }
|
|
+};
|
|
+
|
|
+static int tcp_pkt_to_tuple(const struct sk_buff *skb,
|
|
+ unsigned int dataoff,
|
|
+ struct ip6_conntrack_tuple *tuple)
|
|
+{
|
|
+ struct tcphdr hdr;
|
|
+
|
|
+ /* Actually only need first 8 bytes. */
|
|
+ if (skb_copy_bits(skb, dataoff, &hdr, 8) != 0)
|
|
+ return 0;
|
|
+
|
|
+ tuple->src.u.tcp.port = hdr.source;
|
|
+ tuple->dst.u.tcp.port = hdr.dest;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int tcp_invert_tuple(struct ip6_conntrack_tuple *tuple,
|
|
+ const struct ip6_conntrack_tuple *orig)
|
|
+{
|
|
+ tuple->src.u.tcp.port = orig->dst.u.tcp.port;
|
|
+ tuple->dst.u.tcp.port = orig->src.u.tcp.port;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/* Print out the per-protocol part of the tuple. */
|
|
+static unsigned int tcp_print_tuple(char *buffer,
|
|
+ const struct ip6_conntrack_tuple *tuple)
|
|
+{
|
|
+ return sprintf(buffer, "sport=%hu dport=%hu ",
|
|
+ ntohs(tuple->src.u.tcp.port),
|
|
+ ntohs(tuple->dst.u.tcp.port));
|
|
+}
|
|
+
|
|
+/* Print out the private part of the conntrack. */
|
|
+static unsigned int tcp_print_conntrack(char *buffer,
|
|
+ const struct ip6_conntrack *conntrack)
|
|
+{
|
|
+ enum tcp_conntrack state;
|
|
+
|
|
+ READ_LOCK(&tcp_lock);
|
|
+ state = conntrack->proto.tcp.state;
|
|
+ READ_UNLOCK(&tcp_lock);
|
|
+
|
|
+ return sprintf(buffer, "%s ", tcp_conntrack_names[state]);
|
|
+}
|
|
+
|
|
+static unsigned int get_conntrack_index(const struct tcphdr *tcph)
|
|
+{
|
|
+ if (tcph->rst) return 3;
|
|
+ else if (tcph->syn) return 0;
|
|
+ else if (tcph->fin) return 1;
|
|
+ else if (tcph->ack) return 2;
|
|
+ else return 4;
|
|
+}
|
|
+
|
|
+/* Returns verdict for packet, or -1 for invalid. */
|
|
+static int tcp_packet(struct ip6_conntrack *conntrack,
|
|
+ const struct sk_buff *skb,
|
|
+ unsigned int dataoff,
|
|
+ enum ip6_conntrack_info ctinfo)
|
|
+{
|
|
+ enum tcp_conntrack newconntrack, oldtcpstate;
|
|
+ struct tcphdr tcph;
|
|
+
|
|
+ if (skb_copy_bits(skb, dataoff, &tcph, sizeof(tcph)) != 0)
|
|
+ return -1;
|
|
+
|
|
+ WRITE_LOCK(&tcp_lock);
|
|
+ oldtcpstate = conntrack->proto.tcp.state;
|
|
+ newconntrack
|
|
+ = tcp_conntracks
|
|
+ [CTINFO2DIR(ctinfo)]
|
|
+ [get_conntrack_index(&tcph)][oldtcpstate];
|
|
+
|
|
+ /* Invalid */
|
|
+ if (newconntrack == TCP_CONNTRACK_MAX) {
|
|
+ DEBUGP("ip6_conntrack_tcp: Invalid dir=%i index=%u conntrack=%u\n",
|
|
+ CTINFO2DIR(ctinfo), get_conntrack_index(&tcph),
|
|
+ conntrack->proto.tcp.state);
|
|
+ WRITE_UNLOCK(&tcp_lock);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ conntrack->proto.tcp.state = newconntrack;
|
|
+
|
|
+ /* Poor man's window tracking: record SYN/ACK for handshake check */
|
|
+ if (oldtcpstate == TCP_CONNTRACK_SYN_SENT
|
|
+ && CTINFO2DIR(ctinfo) == IP6_CT_DIR_REPLY
|
|
+ && tcph.syn && tcph.ack)
|
|
+ conntrack->proto.tcp.handshake_ack
|
|
+ = htonl(ntohl(tcph.seq) + 1);
|
|
+
|
|
+ /* If only reply is a RST, we can consider ourselves not to
|
|
+ have an established connection: this is a fairly common
|
|
+ problem case, so we can delete the conntrack
|
|
+ immediately. --RR */
|
|
+ if (!test_bit(IP6S_SEEN_REPLY_BIT, &conntrack->status) && tcph.rst) {
|
|
+ WRITE_UNLOCK(&tcp_lock);
|
|
+ if (del_timer(&conntrack->timeout))
|
|
+ conntrack->timeout.function((unsigned long)conntrack);
|
|
+ } else {
|
|
+ /* Set ASSURED if we see see valid ack in ESTABLISHED after SYN_RECV */
|
|
+ if (oldtcpstate == TCP_CONNTRACK_SYN_RECV
|
|
+ && CTINFO2DIR(ctinfo) == IP6_CT_DIR_ORIGINAL
|
|
+ && tcph.ack && !tcph.syn
|
|
+ && tcph.ack_seq == conntrack->proto.tcp.handshake_ack)
|
|
+ set_bit(IP6S_ASSURED_BIT, &conntrack->status);
|
|
+
|
|
+ WRITE_UNLOCK(&tcp_lock);
|
|
+ ip6_ct_refresh(conntrack, tcp_timeouts[newconntrack]);
|
|
+ }
|
|
+
|
|
+ return NF_ACCEPT;
|
|
+}
|
|
+
|
|
+/* Called when a new connection for this protocol found. */
|
|
+static int tcp_new(struct ip6_conntrack *conntrack, const struct sk_buff *skb,
|
|
+ unsigned int dataoff)
|
|
+{
|
|
+ enum tcp_conntrack newconntrack;
|
|
+ struct tcphdr tcph;
|
|
+
|
|
+ if (skb_copy_bits(skb, dataoff, &tcph, sizeof(tcph)) != 0)
|
|
+ return -1;
|
|
+
|
|
+ /* Don't need lock here: this conntrack not in circulation yet */
|
|
+ newconntrack
|
|
+ = tcp_conntracks[0][get_conntrack_index(&tcph)]
|
|
+ [TCP_CONNTRACK_NONE];
|
|
+
|
|
+ /* Invalid: delete conntrack */
|
|
+ if (newconntrack == TCP_CONNTRACK_MAX) {
|
|
+ DEBUGP("ip6_conntrack_tcp: invalid new deleting.\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ conntrack->proto.tcp.state = newconntrack;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int tcp_exp_matches_pkt(struct ip6_conntrack_expect *exp,
|
|
+ const struct sk_buff *skb,
|
|
+ unsigned int dataoff)
|
|
+{
|
|
+ struct tcphdr tcph;
|
|
+ unsigned int datalen;
|
|
+
|
|
+ if (skb_copy_bits(skb, dataoff, &tcph, sizeof(tcph)) != 0)
|
|
+ return 0;
|
|
+ datalen = skb->len - dataoff;
|
|
+
|
|
+ return between(exp->seq, ntohl(tcph.seq), ntohl(tcph.seq) + datalen);
|
|
+}
|
|
+
|
|
+struct ip6_conntrack_protocol ip6_conntrack_protocol_tcp
|
|
+= { { NULL, NULL }, IPPROTO_TCP, "tcp",
|
|
+ tcp_pkt_to_tuple, tcp_invert_tuple, tcp_print_tuple, tcp_print_conntrack,
|
|
+ tcp_packet, tcp_new, NULL, tcp_exp_matches_pkt, NULL };
|
|
diff -urN linux-2.6.11/net/ipv6/netfilter/ip6_conntrack_proto_udp.c x1/net/ipv6/netfilter/ip6_conntrack_proto_udp.c
|
|
--- linux-2.6.11/net/ipv6/netfilter/ip6_conntrack_proto_udp.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/net/ipv6/netfilter/ip6_conntrack_proto_udp.c 2003-09-18 12:48:46.000000000 +0200
|
|
@@ -0,0 +1,95 @@
|
|
+/*
|
|
+ * UDP extension for IPv6 Connection Tracking
|
|
+ * Linux INET6 implementation
|
|
+ *
|
|
+ * Copyright (C)2003 USAGI/WIDE Project
|
|
+ *
|
|
+ * Authors:
|
|
+ * Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
|
|
+ *
|
|
+ * Based on: net/ipv4/netfilter/ip_conntrack_proto_udp.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.
|
|
+ */
|
|
+#include <linux/types.h>
|
|
+#include <linux/sched.h>
|
|
+#include <linux/timer.h>
|
|
+#include <linux/netfilter.h>
|
|
+#include <linux/udp.h>
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack_protocol.h>
|
|
+
|
|
+#define UDP_TIMEOUT (30*HZ)
|
|
+#define UDP_STREAM_TIMEOUT (180*HZ)
|
|
+
|
|
+static int udp_pkt_to_tuple(const struct sk_buff *skb,
|
|
+ unsigned int dataoff,
|
|
+ struct ip6_conntrack_tuple *tuple)
|
|
+{
|
|
+ struct udphdr hdr;
|
|
+
|
|
+ /* Actually only need first 8 bytes. */
|
|
+ if (skb_copy_bits(skb, dataoff, &hdr, 8) != 0)
|
|
+ return 0;
|
|
+
|
|
+ tuple->src.u.udp.port = hdr.source;
|
|
+ tuple->dst.u.udp.port = hdr.dest;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int udp_invert_tuple(struct ip6_conntrack_tuple *tuple,
|
|
+ const struct ip6_conntrack_tuple *orig)
|
|
+{
|
|
+ tuple->src.u.udp.port = orig->dst.u.udp.port;
|
|
+ tuple->dst.u.udp.port = orig->src.u.udp.port;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/* Print out the per-protocol part of the tuple. */
|
|
+static unsigned int udp_print_tuple(char *buffer,
|
|
+ const struct ip6_conntrack_tuple *tuple)
|
|
+{
|
|
+ return sprintf(buffer, "sport=%hu dport=%hu ",
|
|
+ ntohs(tuple->src.u.udp.port),
|
|
+ ntohs(tuple->dst.u.udp.port));
|
|
+}
|
|
+
|
|
+/* Print out the private part of the conntrack. */
|
|
+static unsigned int udp_print_conntrack(char *buffer,
|
|
+ const struct ip6_conntrack *conntrack)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Returns verdict for packet, and may modify conntracktype */
|
|
+static int udp_packet(struct ip6_conntrack *conntrack,
|
|
+ const struct sk_buff *skb,
|
|
+ unsigned int dataoff,
|
|
+ enum ip6_conntrack_info conntrackinfo)
|
|
+{
|
|
+ /* If we've seen traffic both ways, this is some kind of UDP
|
|
+ stream. Extend timeout. */
|
|
+ if (test_bit(IP6S_SEEN_REPLY_BIT, &conntrack->status)) {
|
|
+ ip6_ct_refresh(conntrack, UDP_STREAM_TIMEOUT);
|
|
+ /* Also, more likely to be important, and not a probe */
|
|
+ set_bit(IP6S_ASSURED_BIT, &conntrack->status);
|
|
+ } else
|
|
+ ip6_ct_refresh(conntrack, UDP_TIMEOUT);
|
|
+
|
|
+ return NF_ACCEPT;
|
|
+}
|
|
+
|
|
+/* Called when a new connection for this protocol found. */
|
|
+static int udp_new(struct ip6_conntrack *conntrack, const struct sk_buff *skb,
|
|
+ unsigned int dataoff)
|
|
+{
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+struct ip6_conntrack_protocol ip6_conntrack_protocol_udp
|
|
+= { { NULL, NULL }, IPPROTO_UDP, "udp",
|
|
+ udp_pkt_to_tuple, udp_invert_tuple, udp_print_tuple, udp_print_conntrack,
|
|
+ udp_packet, udp_new, NULL, NULL, NULL };
|
|
diff -urN linux-2.6.11/net/ipv6/netfilter/ip6_conntrack_reasm.c x1/net/ipv6/netfilter/ip6_conntrack_reasm.c
|
|
--- linux-2.6.11/net/ipv6/netfilter/ip6_conntrack_reasm.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/net/ipv6/netfilter/ip6_conntrack_reasm.c 2003-10-16 04:48:59.000000000 +0200
|
|
@@ -0,0 +1,990 @@
|
|
+/*
|
|
+ * IPv6 fragment reassembly for connection tracking
|
|
+ * Linux INET6 implementation
|
|
+ *
|
|
+ * Copyright (C)2003 USAGI/WIDE Project
|
|
+ *
|
|
+ * Authors:
|
|
+ * Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
|
|
+ *
|
|
+ * Based on: net/ipv6/reassembly.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.
|
|
+ */
|
|
+
|
|
+#include <linux/config.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/types.h>
|
|
+#include <linux/string.h>
|
|
+#include <linux/socket.h>
|
|
+#include <linux/sockios.h>
|
|
+#include <linux/jiffies.h>
|
|
+#include <linux/net.h>
|
|
+#include <linux/list.h>
|
|
+#include <linux/netdevice.h>
|
|
+#include <linux/in6.h>
|
|
+#include <linux/ipv6.h>
|
|
+#include <linux/icmpv6.h>
|
|
+#include <linux/random.h>
|
|
+#include <linux/jhash.h>
|
|
+
|
|
+#include <net/sock.h>
|
|
+#include <net/snmp.h>
|
|
+
|
|
+#include <net/ipv6.h>
|
|
+#include <net/protocol.h>
|
|
+#include <net/transp_v6.h>
|
|
+#include <net/rawv6.h>
|
|
+#include <net/ndisc.h>
|
|
+#include <net/addrconf.h>
|
|
+#include <linux/sysctl.h>
|
|
+#include <linux/netfilter.h>
|
|
+#include <linux/netfilter_ipv6.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+
|
|
+#if 0
|
|
+#define DEBUGP printk
|
|
+#else
|
|
+#define DEBUGP(format, args...)
|
|
+#endif
|
|
+
|
|
+#define IP6CT_FRAGS_HIGH_THRESH 262144 /* == 256*1024 */
|
|
+#define IP6CT_FRAGS_LOW_THRESH 196608 /* == 192*1024 */
|
|
+#define IP6CT_FRAGS_TIMEOUT IPV6_FRAG_TIMEOUT
|
|
+
|
|
+static int sysctl_ip6_ct_frag_high_thresh = 256*1024;
|
|
+static int sysctl_ip6_ct_frag_low_thresh = 192*1024;
|
|
+static int sysctl_ip6_ct_frag_time = IPV6_FRAG_TIMEOUT;
|
|
+
|
|
+struct ip6ct_frag_skb_cb
|
|
+{
|
|
+ struct inet6_skb_parm h;
|
|
+ int offset;
|
|
+ struct sk_buff *orig;
|
|
+};
|
|
+
|
|
+#define IP6CT_FRAG6_CB(skb) ((struct ip6ct_frag_skb_cb*)((skb)->cb))
|
|
+
|
|
+
|
|
+/*
|
|
+ * Equivalent of ipv4 struct ipq
|
|
+ */
|
|
+
|
|
+struct ip6ct_frag_queue
|
|
+{
|
|
+ struct ip6ct_frag_queue *next;
|
|
+ struct list_head lru_list; /* lru list member */
|
|
+
|
|
+ __u32 id; /* fragment id */
|
|
+ struct in6_addr saddr;
|
|
+ struct in6_addr daddr;
|
|
+
|
|
+ spinlock_t lock;
|
|
+ atomic_t refcnt;
|
|
+ struct timer_list timer; /* expire timer */
|
|
+ struct sk_buff *fragments;
|
|
+ int len;
|
|
+ int meat;
|
|
+ struct timeval stamp;
|
|
+ unsigned int csum;
|
|
+ __u8 last_in; /* has first/last segment arrived? */
|
|
+#define COMPLETE 4
|
|
+#define FIRST_IN 2
|
|
+#define LAST_IN 1
|
|
+ __u16 nhoffset;
|
|
+ struct ip6ct_frag_queue **pprev;
|
|
+};
|
|
+
|
|
+/* Hash table. */
|
|
+
|
|
+#define IP6CT_Q_HASHSZ 64
|
|
+
|
|
+static struct ip6ct_frag_queue *ip6_ct_frag_hash[IP6CT_Q_HASHSZ];
|
|
+static rwlock_t ip6_ct_frag_lock = RW_LOCK_UNLOCKED;
|
|
+static u32 ip6_ct_frag_hash_rnd;
|
|
+static LIST_HEAD(ip6_ct_frag_lru_list);
|
|
+int ip6_ct_frag_nqueues = 0;
|
|
+
|
|
+static __inline__ void __fq_unlink(struct ip6ct_frag_queue *fq)
|
|
+{
|
|
+ if(fq->next)
|
|
+ fq->next->pprev = fq->pprev;
|
|
+ *fq->pprev = fq->next;
|
|
+ list_del(&fq->lru_list);
|
|
+ ip6_ct_frag_nqueues--;
|
|
+}
|
|
+
|
|
+static __inline__ void fq_unlink(struct ip6ct_frag_queue *fq)
|
|
+{
|
|
+ write_lock(&ip6_ct_frag_lock);
|
|
+ __fq_unlink(fq);
|
|
+ write_unlock(&ip6_ct_frag_lock);
|
|
+}
|
|
+
|
|
+static unsigned int ip6qhashfn(u32 id, struct in6_addr *saddr,
|
|
+ struct in6_addr *daddr)
|
|
+{
|
|
+ u32 a, b, c;
|
|
+
|
|
+ a = saddr->s6_addr32[0];
|
|
+ b = saddr->s6_addr32[1];
|
|
+ c = saddr->s6_addr32[2];
|
|
+
|
|
+ a += JHASH_GOLDEN_RATIO;
|
|
+ b += JHASH_GOLDEN_RATIO;
|
|
+ c += ip6_ct_frag_hash_rnd;
|
|
+ __jhash_mix(a, b, c);
|
|
+
|
|
+ a += saddr->s6_addr32[3];
|
|
+ b += daddr->s6_addr32[0];
|
|
+ c += daddr->s6_addr32[1];
|
|
+ __jhash_mix(a, b, c);
|
|
+
|
|
+ a += daddr->s6_addr32[2];
|
|
+ b += daddr->s6_addr32[3];
|
|
+ c += id;
|
|
+ __jhash_mix(a, b, c);
|
|
+
|
|
+ return c & (IP6CT_Q_HASHSZ - 1);
|
|
+}
|
|
+
|
|
+static struct timer_list ip6_ct_frag_secret_timer;
|
|
+int sysctl_ip6_ct_frag_secret_interval = 10 * 60 * HZ;
|
|
+
|
|
+static void ip6_ct_frag_secret_rebuild(unsigned long dummy)
|
|
+{
|
|
+ unsigned long now = jiffies;
|
|
+ int i;
|
|
+
|
|
+ write_lock(&ip6_ct_frag_lock);
|
|
+ get_random_bytes(&ip6_ct_frag_hash_rnd, sizeof(u32));
|
|
+ for (i = 0; i < IP6CT_Q_HASHSZ; i++) {
|
|
+ struct ip6ct_frag_queue *q;
|
|
+
|
|
+ q = ip6_ct_frag_hash[i];
|
|
+ while (q) {
|
|
+ struct ip6ct_frag_queue *next = q->next;
|
|
+ unsigned int hval = ip6qhashfn(q->id,
|
|
+ &q->saddr,
|
|
+ &q->daddr);
|
|
+
|
|
+ if (hval != i) {
|
|
+ /* Unlink. */
|
|
+ if (q->next)
|
|
+ q->next->pprev = q->pprev;
|
|
+ *q->pprev = q->next;
|
|
+
|
|
+ /* Relink to new hash chain. */
|
|
+ if ((q->next = ip6_ct_frag_hash[hval]) != NULL)
|
|
+ q->next->pprev = &q->next;
|
|
+ ip6_ct_frag_hash[hval] = q;
|
|
+ q->pprev = &ip6_ct_frag_hash[hval];
|
|
+ }
|
|
+
|
|
+ q = next;
|
|
+ }
|
|
+ }
|
|
+ write_unlock(&ip6_ct_frag_lock);
|
|
+
|
|
+ mod_timer(&ip6_ct_frag_secret_timer, now + sysctl_ip6_ct_frag_secret_interval);
|
|
+}
|
|
+
|
|
+atomic_t ip6_ct_frag_mem = ATOMIC_INIT(0);
|
|
+
|
|
+/* Memory Tracking Functions. */
|
|
+static inline void frag_kfree_skb(struct sk_buff *skb)
|
|
+{
|
|
+ atomic_sub(skb->truesize, &ip6_ct_frag_mem);
|
|
+ if (IP6CT_FRAG6_CB(skb)->orig)
|
|
+ kfree_skb(IP6CT_FRAG6_CB(skb)->orig);
|
|
+
|
|
+ kfree_skb(skb);
|
|
+}
|
|
+
|
|
+static inline void frag_free_queue(struct ip6ct_frag_queue *fq)
|
|
+{
|
|
+ atomic_sub(sizeof(struct ip6ct_frag_queue), &ip6_ct_frag_mem);
|
|
+ kfree(fq);
|
|
+}
|
|
+
|
|
+static inline struct ip6ct_frag_queue *frag_alloc_queue(void)
|
|
+{
|
|
+ struct ip6ct_frag_queue *fq = kmalloc(sizeof(struct ip6ct_frag_queue), GFP_ATOMIC);
|
|
+
|
|
+ if(!fq)
|
|
+ return NULL;
|
|
+ atomic_add(sizeof(struct ip6ct_frag_queue), &ip6_ct_frag_mem);
|
|
+ return fq;
|
|
+}
|
|
+
|
|
+/* Destruction primitives. */
|
|
+
|
|
+/* Complete destruction of fq. */
|
|
+static void ip6_ct_frag_destroy(struct ip6ct_frag_queue *fq)
|
|
+{
|
|
+ struct sk_buff *fp;
|
|
+
|
|
+ BUG_TRAP(fq->last_in&COMPLETE);
|
|
+ BUG_TRAP(del_timer(&fq->timer) == 0);
|
|
+
|
|
+ /* Release all fragment data. */
|
|
+ fp = fq->fragments;
|
|
+ while (fp) {
|
|
+ struct sk_buff *xp = fp->next;
|
|
+
|
|
+ frag_kfree_skb(fp);
|
|
+ fp = xp;
|
|
+ }
|
|
+
|
|
+ frag_free_queue(fq);
|
|
+}
|
|
+
|
|
+static __inline__ void fq_put(struct ip6ct_frag_queue *fq)
|
|
+{
|
|
+ if (atomic_dec_and_test(&fq->refcnt))
|
|
+ ip6_ct_frag_destroy(fq);
|
|
+}
|
|
+
|
|
+/* Kill fq entry. It is not destroyed immediately,
|
|
+ * because caller (and someone more) holds reference count.
|
|
+ */
|
|
+static __inline__ void fq_kill(struct ip6ct_frag_queue *fq)
|
|
+{
|
|
+ if (del_timer(&fq->timer))
|
|
+ atomic_dec(&fq->refcnt);
|
|
+
|
|
+ if (!(fq->last_in & COMPLETE)) {
|
|
+ fq_unlink(fq);
|
|
+ atomic_dec(&fq->refcnt);
|
|
+ fq->last_in |= COMPLETE;
|
|
+ }
|
|
+}
|
|
+
|
|
+static void ip6_ct_frag_evictor(void)
|
|
+{
|
|
+ struct ip6ct_frag_queue *fq;
|
|
+ struct list_head *tmp;
|
|
+
|
|
+ for(;;) {
|
|
+ if (atomic_read(&ip6_ct_frag_mem) <= sysctl_ip6_ct_frag_low_thresh)
|
|
+ return;
|
|
+ read_lock(&ip6_ct_frag_lock);
|
|
+ if (list_empty(&ip6_ct_frag_lru_list)) {
|
|
+ read_unlock(&ip6_ct_frag_lock);
|
|
+ return;
|
|
+ }
|
|
+ tmp = ip6_ct_frag_lru_list.next;
|
|
+ fq = list_entry(tmp, struct ip6ct_frag_queue, lru_list);
|
|
+ atomic_inc(&fq->refcnt);
|
|
+ read_unlock(&ip6_ct_frag_lock);
|
|
+
|
|
+ spin_lock(&fq->lock);
|
|
+ if (!(fq->last_in&COMPLETE))
|
|
+ fq_kill(fq);
|
|
+ spin_unlock(&fq->lock);
|
|
+
|
|
+ fq_put(fq);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void ip6_ct_frag_expire(unsigned long data)
|
|
+{
|
|
+ struct ip6ct_frag_queue *fq = (struct ip6ct_frag_queue *) data;
|
|
+
|
|
+ spin_lock(&fq->lock);
|
|
+
|
|
+ if (fq->last_in & COMPLETE)
|
|
+ goto out;
|
|
+
|
|
+ fq_kill(fq);
|
|
+
|
|
+out:
|
|
+ spin_unlock(&fq->lock);
|
|
+ fq_put(fq);
|
|
+}
|
|
+
|
|
+/* Creation primitives. */
|
|
+
|
|
+
|
|
+static struct ip6ct_frag_queue *ip6_ct_frag_intern(unsigned int hash,
|
|
+ struct ip6ct_frag_queue *fq_in)
|
|
+{
|
|
+ struct ip6ct_frag_queue *fq;
|
|
+
|
|
+ write_lock(&ip6_ct_frag_lock);
|
|
+#ifdef CONFIG_SMP
|
|
+ for (fq = ip6_ct_frag_hash[hash]; fq; fq = fq->next) {
|
|
+ if (fq->id == fq_in->id &&
|
|
+ !ipv6_addr_cmp(&fq_in->saddr, &fq->saddr) &&
|
|
+ !ipv6_addr_cmp(&fq_in->daddr, &fq->daddr)) {
|
|
+ atomic_inc(&fq->refcnt);
|
|
+ write_unlock(&ip6_ct_frag_lock);
|
|
+ fq_in->last_in |= COMPLETE;
|
|
+ fq_put(fq_in);
|
|
+ return fq;
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+ fq = fq_in;
|
|
+
|
|
+ if (!mod_timer(&fq->timer, jiffies + sysctl_ip6_ct_frag_time))
|
|
+ atomic_inc(&fq->refcnt);
|
|
+
|
|
+ atomic_inc(&fq->refcnt);
|
|
+ if((fq->next = ip6_ct_frag_hash[hash]) != NULL)
|
|
+ fq->next->pprev = &fq->next;
|
|
+ ip6_ct_frag_hash[hash] = fq;
|
|
+ fq->pprev = &ip6_ct_frag_hash[hash];
|
|
+ INIT_LIST_HEAD(&fq->lru_list);
|
|
+ list_add_tail(&fq->lru_list, &ip6_ct_frag_lru_list);
|
|
+ ip6_ct_frag_nqueues++;
|
|
+ write_unlock(&ip6_ct_frag_lock);
|
|
+ return fq;
|
|
+}
|
|
+
|
|
+
|
|
+static struct ip6ct_frag_queue *
|
|
+ip6_ct_frag_create(unsigned int hash, u32 id, struct in6_addr *src, struct in6_addr *dst)
|
|
+{
|
|
+ struct ip6ct_frag_queue *fq;
|
|
+
|
|
+ if ((fq = frag_alloc_queue()) == NULL) {
|
|
+ DEBUGP("Can't alloc new queue\n");
|
|
+ goto oom;
|
|
+ }
|
|
+
|
|
+ memset(fq, 0, sizeof(struct ip6ct_frag_queue));
|
|
+
|
|
+ fq->id = id;
|
|
+ ipv6_addr_copy(&fq->saddr, src);
|
|
+ ipv6_addr_copy(&fq->daddr, dst);
|
|
+
|
|
+ init_timer(&fq->timer);
|
|
+ fq->timer.function = ip6_ct_frag_expire;
|
|
+ fq->timer.data = (long) fq;
|
|
+ fq->lock = SPIN_LOCK_UNLOCKED;
|
|
+ atomic_set(&fq->refcnt, 1);
|
|
+
|
|
+ return ip6_ct_frag_intern(hash, fq);
|
|
+
|
|
+oom:
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static __inline__ struct ip6ct_frag_queue *
|
|
+fq_find(u32 id, struct in6_addr *src, struct in6_addr *dst)
|
|
+{
|
|
+ struct ip6ct_frag_queue *fq;
|
|
+ unsigned int hash = ip6qhashfn(id, src, dst);
|
|
+
|
|
+ read_lock(&ip6_ct_frag_lock);
|
|
+ for(fq = ip6_ct_frag_hash[hash]; fq; fq = fq->next) {
|
|
+ if (fq->id == id &&
|
|
+ !ipv6_addr_cmp(src, &fq->saddr) &&
|
|
+ !ipv6_addr_cmp(dst, &fq->daddr)) {
|
|
+ atomic_inc(&fq->refcnt);
|
|
+ read_unlock(&ip6_ct_frag_lock);
|
|
+ return fq;
|
|
+ }
|
|
+ }
|
|
+ read_unlock(&ip6_ct_frag_lock);
|
|
+
|
|
+ return ip6_ct_frag_create(hash, id, src, dst);
|
|
+}
|
|
+
|
|
+
|
|
+static int ip6_ct_frag_queue(struct ip6ct_frag_queue *fq, struct sk_buff *skb,
|
|
+ struct frag_hdr *fhdr, int nhoff)
|
|
+{
|
|
+ struct sk_buff *prev, *next;
|
|
+ int offset, end;
|
|
+
|
|
+ if (fq->last_in & COMPLETE) {
|
|
+ DEBUGP("Allready completed\n");
|
|
+ goto err;
|
|
+ }
|
|
+
|
|
+ offset = ntohs(fhdr->frag_off) & ~0x7;
|
|
+ end = offset + (ntohs(skb->nh.ipv6h->payload_len) -
|
|
+ ((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1)));
|
|
+
|
|
+ if ((unsigned int)end > IPV6_MAXPLEN) {
|
|
+ DEBUGP("offset is too large.\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ if (skb->ip_summed == CHECKSUM_HW)
|
|
+ skb->csum = csum_sub(skb->csum,
|
|
+ csum_partial(skb->nh.raw, (u8*)(fhdr+1)-skb->nh.raw, 0));
|
|
+
|
|
+ /* Is this the final fragment? */
|
|
+ if (!(fhdr->frag_off & htons(IP6_MF))) {
|
|
+ /* If we already have some bits beyond end
|
|
+ * or have different end, the segment is corrupted.
|
|
+ */
|
|
+ if (end < fq->len ||
|
|
+ ((fq->last_in & LAST_IN) && end != fq->len)) {
|
|
+ DEBUGP("already received last fragment\n");
|
|
+ goto err;
|
|
+ }
|
|
+ fq->last_in |= LAST_IN;
|
|
+ fq->len = end;
|
|
+ } else {
|
|
+ /* Check if the fragment is rounded to 8 bytes.
|
|
+ * Required by the RFC.
|
|
+ */
|
|
+ if (end & 0x7) {
|
|
+ /* RFC2460 says always send parameter problem in
|
|
+ * this case. -DaveM
|
|
+ */
|
|
+ DEBUGP("the end of this message is not rounded to 8 bytes.\n");
|
|
+ return -1;
|
|
+ }
|
|
+ if (end > fq->len) {
|
|
+ /* Some bits beyond end -> corruption. */
|
|
+ if (fq->last_in & LAST_IN) {
|
|
+ DEBUGP("last packet already reached.\n");
|
|
+ goto err;
|
|
+ }
|
|
+ fq->len = end;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (end == offset)
|
|
+ goto err;
|
|
+
|
|
+ /* Point into the IP datagram 'data' part. */
|
|
+ if (!pskb_pull(skb, (u8 *) (fhdr + 1) - skb->data)) {
|
|
+ DEBUGP("queue: message is too short.\n");
|
|
+ goto err;
|
|
+ }
|
|
+ if (end-offset < skb->len) {
|
|
+ if (pskb_trim(skb, end - offset)) {
|
|
+ DEBUGP("Can't trim\n");
|
|
+ goto err;
|
|
+ }
|
|
+ if (skb->ip_summed != CHECKSUM_UNNECESSARY)
|
|
+ skb->ip_summed = CHECKSUM_NONE;
|
|
+ }
|
|
+
|
|
+ /* Find out which fragments are in front and at the back of us
|
|
+ * in the chain of fragments so far. We must know where to put
|
|
+ * this fragment, right?
|
|
+ */
|
|
+ prev = NULL;
|
|
+ for(next = fq->fragments; next != NULL; next = next->next) {
|
|
+ if (IP6CT_FRAG6_CB(next)->offset >= offset)
|
|
+ break; /* bingo! */
|
|
+ prev = next;
|
|
+ }
|
|
+
|
|
+ /* We found where to put this one. Check for overlap with
|
|
+ * preceding fragment, and, if needed, align things so that
|
|
+ * any overlaps are eliminated.
|
|
+ */
|
|
+ if (prev) {
|
|
+ int i = (IP6CT_FRAG6_CB(prev)->offset + prev->len) - offset;
|
|
+
|
|
+ if (i > 0) {
|
|
+ offset += i;
|
|
+ if (end <= offset) {
|
|
+ DEBUGP("overlap\n");
|
|
+ goto err;
|
|
+ }
|
|
+ if (!pskb_pull(skb, i)) {
|
|
+ DEBUGP("Can't pull\n");
|
|
+ goto err;
|
|
+ }
|
|
+ if (skb->ip_summed != CHECKSUM_UNNECESSARY)
|
|
+ skb->ip_summed = CHECKSUM_NONE;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Look for overlap with succeeding segments.
|
|
+ * If we can merge fragments, do it.
|
|
+ */
|
|
+ while (next && IP6CT_FRAG6_CB(next)->offset < end) {
|
|
+ int i = end - IP6CT_FRAG6_CB(next)->offset; /* overlap is 'i' bytes */
|
|
+
|
|
+ if (i < next->len) {
|
|
+ /* Eat head of the next overlapped fragment
|
|
+ * and leave the loop. The next ones cannot overlap.
|
|
+ */
|
|
+ DEBUGP("Eat head of the overlapped parts.: %d", i);
|
|
+ if (!pskb_pull(next, i))
|
|
+ goto err;
|
|
+ IP6CT_FRAG6_CB(next)->offset += i; /* next fragment */
|
|
+ fq->meat -= i;
|
|
+ if (next->ip_summed != CHECKSUM_UNNECESSARY)
|
|
+ next->ip_summed = CHECKSUM_NONE;
|
|
+ break;
|
|
+ } else {
|
|
+ struct sk_buff *free_it = next;
|
|
+
|
|
+ /* Old fragmnet is completely overridden with
|
|
+ * new one drop it.
|
|
+ */
|
|
+ next = next->next;
|
|
+
|
|
+ if (prev)
|
|
+ prev->next = next;
|
|
+ else
|
|
+ fq->fragments = next;
|
|
+
|
|
+ fq->meat -= free_it->len;
|
|
+ frag_kfree_skb(free_it);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ IP6CT_FRAG6_CB(skb)->offset = offset;
|
|
+
|
|
+ /* Insert this fragment in the chain of fragments. */
|
|
+ skb->next = next;
|
|
+ if (prev)
|
|
+ prev->next = skb;
|
|
+ else
|
|
+ fq->fragments = skb;
|
|
+
|
|
+ skb->dev = NULL;
|
|
+ fq->stamp = skb->stamp;
|
|
+ fq->meat += skb->len;
|
|
+ atomic_add(skb->truesize, &ip6_ct_frag_mem);
|
|
+
|
|
+ /* The first fragment.
|
|
+ * nhoffset is obtained from the first fragment, of course.
|
|
+ */
|
|
+ if (offset == 0) {
|
|
+ fq->nhoffset = nhoff;
|
|
+ fq->last_in |= FIRST_IN;
|
|
+ }
|
|
+ write_lock(&ip6_ct_frag_lock);
|
|
+ list_move_tail(&fq->lru_list, &ip6_ct_frag_lru_list);
|
|
+ write_unlock(&ip6_ct_frag_lock);
|
|
+ return 0;
|
|
+
|
|
+err:
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Check if this packet is complete.
|
|
+ * Returns NULL on failure by any reason, and pointer
|
|
+ * to current nexthdr field in reassembled frame.
|
|
+ *
|
|
+ * It is called with locked fq, and caller must check that
|
|
+ * queue is eligible for reassembly i.e. it is not COMPLETE,
|
|
+ * the last and the first frames arrived and all the bits are here.
|
|
+ */
|
|
+static struct sk_buff *
|
|
+ip6_ct_frag_reasm(struct ip6ct_frag_queue *fq, struct net_device *dev)
|
|
+{
|
|
+ struct sk_buff *fp, *op, *head = fq->fragments;
|
|
+ int payload_len;
|
|
+
|
|
+ fq_kill(fq);
|
|
+
|
|
+ BUG_TRAP(head != NULL);
|
|
+ BUG_TRAP(IP6CT_FRAG6_CB(head)->offset == 0);
|
|
+
|
|
+ /* Unfragmented part is taken from the first segment. */
|
|
+ payload_len = (head->data - head->nh.raw) - sizeof(struct ipv6hdr) + fq->len - sizeof(struct frag_hdr);
|
|
+ if (payload_len > IPV6_MAXPLEN) {
|
|
+ DEBUGP("payload len is too large.\n");
|
|
+ goto out_oversize;
|
|
+ }
|
|
+
|
|
+ /* Head of list must not be cloned. */
|
|
+ if (skb_cloned(head) && pskb_expand_head(head, 0, 0, GFP_ATOMIC)) {
|
|
+ DEBUGP("skb is cloned but can't expand head");
|
|
+ goto out_oom;
|
|
+ }
|
|
+
|
|
+ /* If the first fragment is fragmented itself, we split
|
|
+ * it to two chunks: the first with data and paged part
|
|
+ * and the second, holding only fragments. */
|
|
+ if (skb_shinfo(head)->frag_list) {
|
|
+ struct sk_buff *clone;
|
|
+ int i, plen = 0;
|
|
+
|
|
+ if ((clone = alloc_skb(0, GFP_ATOMIC)) == NULL) {
|
|
+ DEBUGP("Can't alloc skb\n");
|
|
+ goto out_oom;
|
|
+ }
|
|
+ clone->next = head->next;
|
|
+ head->next = clone;
|
|
+ skb_shinfo(clone)->frag_list = skb_shinfo(head)->frag_list;
|
|
+ skb_shinfo(head)->frag_list = NULL;
|
|
+ for (i=0; i<skb_shinfo(head)->nr_frags; i++)
|
|
+ plen += skb_shinfo(head)->frags[i].size;
|
|
+ clone->len = clone->data_len = head->data_len - plen;
|
|
+ head->data_len -= clone->len;
|
|
+ head->len -= clone->len;
|
|
+ clone->csum = 0;
|
|
+ clone->ip_summed = head->ip_summed;
|
|
+
|
|
+ IP6CT_FRAG6_CB(clone)->orig = NULL;
|
|
+ atomic_add(clone->truesize, &ip6_ct_frag_mem);
|
|
+ }
|
|
+
|
|
+ /* We have to remove fragment header from datagram and to relocate
|
|
+ * header in order to calculate ICV correctly. */
|
|
+ head->nh.raw[fq->nhoffset] = head->h.raw[0];
|
|
+ memmove(head->head + sizeof(struct frag_hdr), head->head,
|
|
+ (head->data - head->head) - sizeof(struct frag_hdr));
|
|
+ head->mac.raw += sizeof(struct frag_hdr);
|
|
+ head->nh.raw += sizeof(struct frag_hdr);
|
|
+
|
|
+ skb_shinfo(head)->frag_list = head->next;
|
|
+ head->h.raw = head->data;
|
|
+ skb_push(head, head->data - head->nh.raw);
|
|
+ atomic_sub(head->truesize, &ip6_ct_frag_mem);
|
|
+
|
|
+ for (fp=head->next; fp; fp = fp->next) {
|
|
+ head->data_len += fp->len;
|
|
+ head->len += fp->len;
|
|
+ if (head->ip_summed != fp->ip_summed)
|
|
+ head->ip_summed = CHECKSUM_NONE;
|
|
+ else if (head->ip_summed == CHECKSUM_HW)
|
|
+ head->csum = csum_add(head->csum, fp->csum);
|
|
+ head->truesize += fp->truesize;
|
|
+ atomic_sub(fp->truesize, &ip6_ct_frag_mem);
|
|
+ }
|
|
+
|
|
+ head->next = NULL;
|
|
+ head->dev = dev;
|
|
+ head->stamp = fq->stamp;
|
|
+ head->nh.ipv6h->payload_len = ntohs(payload_len);
|
|
+
|
|
+ /* Yes, and fold redundant checksum back. 8) */
|
|
+ if (head->ip_summed == CHECKSUM_HW)
|
|
+ head->csum = csum_partial(head->nh.raw, head->h.raw-head->nh.raw, head->csum);
|
|
+
|
|
+ fq->fragments = NULL;
|
|
+
|
|
+ /* all original skbs are linked into the IP6CT_FRAG6_CB(head).orig */
|
|
+ fp = skb_shinfo(head)->frag_list;
|
|
+ if (IP6CT_FRAG6_CB(fp)->orig == NULL)
|
|
+ /* at above code, head skb is divided into two skbs. */
|
|
+ fp = fp->next;
|
|
+
|
|
+ op = IP6CT_FRAG6_CB(head)->orig;
|
|
+ for (; fp; fp = fp->next) {
|
|
+ struct sk_buff *orig = IP6CT_FRAG6_CB(fp)->orig;
|
|
+
|
|
+ op->next = orig;
|
|
+ op = orig;
|
|
+ IP6CT_FRAG6_CB(fp)->orig = NULL;
|
|
+ }
|
|
+
|
|
+ return head;
|
|
+
|
|
+out_oversize:
|
|
+ if (net_ratelimit())
|
|
+ printk(KERN_DEBUG "ip6_ct_frag_reasm: payload len = %d\n", payload_len);
|
|
+ goto out_fail;
|
|
+out_oom:
|
|
+ if (net_ratelimit())
|
|
+ printk(KERN_DEBUG "ip6_ct_frag_reasm: no memory for reassembly\n");
|
|
+out_fail:
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * find the header just before Fragment Header.
|
|
+ *
|
|
+ * if success return 0 and set ...
|
|
+ * (*prevhdrp): the value of "Next Header Field" in the header
|
|
+ * just before Fragment Header.
|
|
+ * (*prevhoff): the offset of "Next Header Field" in the header
|
|
+ * just before Fragment Header.
|
|
+ * (*fhoff) : the offset of Fragment Header.
|
|
+ *
|
|
+ * Based on ipv6_skip_hdr() in net/ipv6/exthdr.c
|
|
+ *
|
|
+ */
|
|
+static int
|
|
+find_prev_fhdr(struct sk_buff *skb, u8 *prevhdrp, int *prevhoff, int *fhoff)
|
|
+{
|
|
+ u8 nexthdr = skb->nh.ipv6h->nexthdr;
|
|
+ u8 prev_nhoff = (u8 *)&skb->nh.ipv6h->nexthdr - skb->data;
|
|
+ int start = (u8 *)(skb->nh.ipv6h+1) - skb->data;
|
|
+ int len = skb->len - start;
|
|
+ u8 prevhdr = NEXTHDR_IPV6;
|
|
+
|
|
+ while (nexthdr != NEXTHDR_FRAGMENT) {
|
|
+ struct ipv6_opt_hdr hdr;
|
|
+ int hdrlen;
|
|
+
|
|
+ if (!ipv6_ext_hdr(nexthdr)) {
|
|
+ return -1;
|
|
+ }
|
|
+ if (len < (int)sizeof(struct ipv6_opt_hdr)) {
|
|
+ DEBUGP("too short\n");
|
|
+ return -1;
|
|
+ }
|
|
+ if (nexthdr == NEXTHDR_NONE) {
|
|
+ DEBUGP("next header is none\n");
|
|
+ return -1;
|
|
+ }
|
|
+ if (skb_copy_bits(skb, start, &hdr, sizeof(hdr)))
|
|
+ BUG();
|
|
+ if (nexthdr == NEXTHDR_AUTH)
|
|
+ hdrlen = (hdr.hdrlen+2)<<2;
|
|
+ else
|
|
+ hdrlen = ipv6_optlen(&hdr);
|
|
+
|
|
+ prevhdr = nexthdr;
|
|
+ prev_nhoff = start;
|
|
+
|
|
+ nexthdr = hdr.nexthdr;
|
|
+ len -= hdrlen;
|
|
+ start += hdrlen;
|
|
+ }
|
|
+
|
|
+ if (len < 0)
|
|
+ return -1;
|
|
+
|
|
+ *prevhdrp = prevhdr;
|
|
+ *prevhoff = prev_nhoff;
|
|
+ *fhoff = start;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+struct sk_buff *ip6_ct_gather_frags(struct sk_buff *skb)
|
|
+{
|
|
+ struct sk_buff *clone;
|
|
+ struct net_device *dev = skb->dev;
|
|
+ struct frag_hdr *fhdr;
|
|
+ struct ip6ct_frag_queue *fq;
|
|
+ struct ipv6hdr *hdr;
|
|
+ int fhoff, nhoff;
|
|
+ u8 prevhdr;
|
|
+ struct sk_buff *ret_skb = NULL;
|
|
+
|
|
+ /* Jumbo payload inhibits frag. header */
|
|
+ if (skb->nh.ipv6h->payload_len == 0) {
|
|
+ DEBUGP("payload len = 0\n");
|
|
+ return skb;
|
|
+ }
|
|
+
|
|
+ if (find_prev_fhdr(skb, &prevhdr, &nhoff, &fhoff) < 0)
|
|
+ return skb;
|
|
+
|
|
+ clone = skb_clone(skb, GFP_ATOMIC);
|
|
+ if (clone == NULL) {
|
|
+ DEBUGP("Can't clone skb\n");
|
|
+ return skb;
|
|
+ }
|
|
+
|
|
+ IP6CT_FRAG6_CB(clone)->orig = skb;
|
|
+
|
|
+ if (!pskb_may_pull(clone, fhoff + sizeof(*fhdr))) {
|
|
+ DEBUGP("message is too short.\n");
|
|
+ goto ret_orig;
|
|
+ }
|
|
+
|
|
+ clone->h.raw = clone->data + fhoff;
|
|
+ hdr = clone->nh.ipv6h;
|
|
+ fhdr = (struct frag_hdr *)clone->h.raw;
|
|
+
|
|
+ if (!(fhdr->frag_off & htons(0xFFF9))) {
|
|
+ DEBUGP("Invalid fragment offset\n");
|
|
+ /* It is not a fragmented frame */
|
|
+ goto ret_orig;
|
|
+ }
|
|
+
|
|
+ if (atomic_read(&ip6_ct_frag_mem) > sysctl_ip6_ct_frag_high_thresh)
|
|
+ ip6_ct_frag_evictor();
|
|
+
|
|
+ if ((fq = fq_find(fhdr->identification, &hdr->saddr, &hdr->daddr)) == NULL) {
|
|
+ DEBUGP("Can't find and can't create new queue\n");
|
|
+ goto ret_orig;
|
|
+ }
|
|
+
|
|
+ spin_lock(&fq->lock);
|
|
+
|
|
+ if (ip6_ct_frag_queue(fq, clone, fhdr, nhoff) < 0) {
|
|
+ spin_unlock(&fq->lock);
|
|
+ DEBUGP("Can't insert skb to queue\n");
|
|
+ fq_put(fq);
|
|
+ goto ret_orig;
|
|
+ }
|
|
+
|
|
+ if (fq->last_in == (FIRST_IN|LAST_IN) &&
|
|
+ fq->meat == fq->len) {
|
|
+ ret_skb = ip6_ct_frag_reasm(fq, dev);
|
|
+
|
|
+ if (ret_skb == NULL)
|
|
+ DEBUGP("Can't reassemble fragmented packets\n");
|
|
+ }
|
|
+ spin_unlock(&fq->lock);
|
|
+
|
|
+ fq_put(fq);
|
|
+ return ret_skb;
|
|
+
|
|
+ret_orig:
|
|
+ kfree_skb(clone);
|
|
+ return skb;
|
|
+}
|
|
+
|
|
+int ip6_ct_output_frags(struct sk_buff *skb, struct nf_info *info)
|
|
+{
|
|
+ struct sk_buff *s, *s2;
|
|
+ struct nf_info *copy_info;
|
|
+
|
|
+ for (s = IP6CT_FRAG6_CB(skb)->orig; s;) {
|
|
+ if (skb->nfct)
|
|
+ nf_conntrack_get(skb->nfct);
|
|
+ s->nfct = skb->nfct;
|
|
+ s->nfcache = skb->nfcache;
|
|
+
|
|
+ /*
|
|
+ * nf_reinject() frees copy_info,
|
|
+ * so I have to copy it every time. (T-T
|
|
+ */
|
|
+ copy_info = kmalloc(sizeof(*copy_info), GFP_ATOMIC);
|
|
+ if (copy_info == NULL) {
|
|
+ DEBUGP("Can't kmalloc() for nf_info\n");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ copy_info->pf = info->pf;
|
|
+ copy_info->hook = info->hook;
|
|
+ copy_info->indev = info->indev;
|
|
+ copy_info->outdev = info->outdev;
|
|
+ copy_info->okfn = info->okfn;
|
|
+ copy_info->elem = info->elem;
|
|
+
|
|
+ /*
|
|
+ * nf_reinject() put the module "ip6_conntrack".
|
|
+ */
|
|
+ if (!try_module_get(info->elem->owner)) {
|
|
+ DEBUGP("Can't get module.\n");
|
|
+ kfree_skb(s);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (copy_info->indev)
|
|
+ dev_hold(copy_info->indev);
|
|
+ if (copy_info->outdev)
|
|
+ dev_hold(copy_info->outdev);
|
|
+
|
|
+ s2 = s->next;
|
|
+ nf_reinject(s, copy_info, NF_ACCEPT);
|
|
+ s = s2;
|
|
+ }
|
|
+
|
|
+ kfree_skb(skb);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int ip6_ct_kfree_frags(struct sk_buff *skb)
|
|
+{
|
|
+ struct sk_buff *s, *s2;
|
|
+
|
|
+ for (s = IP6CT_FRAG6_CB(skb)->orig; s; s = s2) {
|
|
+
|
|
+ s2 = s->next;
|
|
+ kfree_skb(s);
|
|
+ }
|
|
+
|
|
+ kfree_skb(skb);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_SYSCTL
|
|
+
|
|
+#define IP6CT_HIGH_THRESH_NAME "ip6ct_frags_high_thresh"
|
|
+#define IP6CT_LOW_THRESH_NAME "ip6ct_frags_low_thresh"
|
|
+#define IP6CT_TIMEOUT_NAME "ip6ct_frags_timeout"
|
|
+
|
|
+static struct ctl_table_header *ip6_ct_frags_sysctl_header;
|
|
+
|
|
+static ctl_table ip6_ct_frags_table[] = {
|
|
+ {
|
|
+ .ctl_name = IP6CT_FRAGS_HIGH_THRESH,
|
|
+ .procname = IP6CT_HIGH_THRESH_NAME,
|
|
+ .data = &sysctl_ip6_ct_frag_high_thresh,
|
|
+ .maxlen = sizeof(sysctl_ip6_ct_frag_high_thresh),
|
|
+ .mode = 0644,
|
|
+ .proc_handler = proc_dointvec
|
|
+ },
|
|
+ {
|
|
+ .ctl_name = IP6CT_FRAGS_LOW_THRESH,
|
|
+ .procname = IP6CT_LOW_THRESH_NAME,
|
|
+ .data = &sysctl_ip6_ct_frag_low_thresh,
|
|
+ .maxlen = sizeof(sysctl_ip6_ct_frag_high_thresh),
|
|
+ .mode = 0644,
|
|
+ .proc_handler = proc_dointvec
|
|
+ },
|
|
+ {
|
|
+ .ctl_name = IP6CT_FRAGS_TIMEOUT,
|
|
+ .procname = IP6CT_TIMEOUT_NAME,
|
|
+ .data = &sysctl_ip6_ct_frag_time,
|
|
+ .maxlen = sizeof(sysctl_ip6_ct_frag_time),
|
|
+ .mode = 0644,
|
|
+ .proc_handler = proc_dointvec
|
|
+ },
|
|
+ { .ctl_name = 0 }
|
|
+};
|
|
+
|
|
+static ctl_table ip6_ct_frags_dir_table[] = {
|
|
+ {
|
|
+ .ctl_name = NET_IPV6,
|
|
+ .procname = "ipv6", NULL,
|
|
+ .mode = 0555,
|
|
+ .child = ip6_ct_frags_table
|
|
+ },
|
|
+ { .ctl_name = 0 }
|
|
+};
|
|
+
|
|
+static ctl_table ip6_ct_frags_root_table[] = {
|
|
+ {
|
|
+ .ctl_name = CTL_NET,
|
|
+ .procname = "net",
|
|
+ .mode = 0555,
|
|
+ .child = ip6_ct_frags_dir_table
|
|
+ },
|
|
+ { .ctl_name = 0 }
|
|
+};
|
|
+
|
|
+#endif /*CONFIG_SYSCTL*/
|
|
+
|
|
+int __init ip6_ct_frags_init(void)
|
|
+{
|
|
+#ifdef CONFIG_SYSCTL
|
|
+ ip6_ct_frags_sysctl_header = register_sysctl_table(ip6_ct_frags_root_table, 0);
|
|
+
|
|
+ if (ip6_ct_frags_sysctl_header == NULL) {
|
|
+ printk("ip6_ct_frags_init: Can't register sysctl tables.\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+#endif
|
|
+
|
|
+ ip6_ct_frag_hash_rnd = (u32) ((num_physpages ^ (num_physpages>>7)) ^
|
|
+ (jiffies ^ (jiffies >> 6)));
|
|
+
|
|
+ init_timer(&ip6_ct_frag_secret_timer);
|
|
+ ip6_ct_frag_secret_timer.function = ip6_ct_frag_secret_rebuild;
|
|
+ ip6_ct_frag_secret_timer.expires = jiffies + sysctl_ip6_ct_frag_secret_interval;
|
|
+ add_timer(&ip6_ct_frag_secret_timer);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void ip6_ct_frags_cleanup(void)
|
|
+{
|
|
+ del_timer(&ip6_ct_frag_secret_timer);
|
|
+#ifdef CONFIG_SYSCTL
|
|
+ unregister_sysctl_table(ip6_ct_frags_sysctl_header);
|
|
+#endif
|
|
+ sysctl_ip6_ct_frag_low_thresh = 0;
|
|
+ ip6_ct_frag_evictor();
|
|
+}
|
|
diff -urN linux-2.6.11/net/ipv6/netfilter/ip6_conntrack_standalone.c x1/net/ipv6/netfilter/ip6_conntrack_standalone.c
|
|
--- linux-2.6.11/net/ipv6/netfilter/ip6_conntrack_standalone.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/net/ipv6/netfilter/ip6_conntrack_standalone.c 2003-09-20 11:00:21.000000000 +0200
|
|
@@ -0,0 +1,502 @@
|
|
+/*
|
|
+ * IPv6 Connection Tracking
|
|
+ * Linux INET6 implementation
|
|
+ *
|
|
+ * Copyright (C)2003 USAGI/WIDE Project
|
|
+ *
|
|
+ * Authors:
|
|
+ * Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
|
|
+ *
|
|
+ * Based on: net/ipv4/netfilter/ip_conntrack_standalone.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.
|
|
+ */
|
|
+
|
|
+/* This file contains all the functions required for the standalone
|
|
+ ip6_conntrack module.
|
|
+
|
|
+ These are not required by the compatibility layer.
|
|
+*/
|
|
+
|
|
+/* (c) 1999 Paul `Rusty' Russell. Licenced under the GNU General
|
|
+ Public Licence. */
|
|
+
|
|
+#include <linux/types.h>
|
|
+#include <linux/ipv6.h>
|
|
+#include <linux/netfilter.h>
|
|
+#include <linux/netfilter_ipv6.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/skbuff.h>
|
|
+#include <linux/proc_fs.h>
|
|
+#include <linux/version.h>
|
|
+#include <net/checksum.h>
|
|
+
|
|
+#define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip6_conntrack_lock)
|
|
+#define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip6_conntrack_lock)
|
|
+
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack.h>
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack_protocol.h>
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack_core.h>
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack_helper.h>
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack_reasm.h>
|
|
+#include <linux/netfilter_ipv4/listhelp.h>
|
|
+
|
|
+#if 0
|
|
+#define DEBUGP printk
|
|
+#else
|
|
+#define DEBUGP(format, args...)
|
|
+#endif
|
|
+
|
|
+MODULE_LICENSE("GPL");
|
|
+
|
|
+static int kill_proto(const struct ip6_conntrack *i, void *data)
|
|
+{
|
|
+ return (i->tuplehash[IP6_CT_DIR_ORIGINAL].tuple.dst.protonum ==
|
|
+ *((u_int8_t *) data));
|
|
+}
|
|
+
|
|
+static unsigned int
|
|
+print_tuple(char *buffer, const struct ip6_conntrack_tuple *tuple,
|
|
+ struct ip6_conntrack_protocol *proto)
|
|
+{
|
|
+ int len;
|
|
+
|
|
+ len = sprintf(buffer, "src=%x:%x:%x:%x:%x:%x:%x:%x dst=%x:%x:%x:%x:%x:%x:%x:%x ",
|
|
+ NIP6(tuple->src.ip), NIP6(tuple->dst.ip));
|
|
+
|
|
+ len += proto->print_tuple(buffer + len, tuple);
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+/* FIXME: Don't print source proto part. --RR */
|
|
+static unsigned int
|
|
+print_expect(char *buffer, const struct ip6_conntrack_expect *expect)
|
|
+{
|
|
+ unsigned int len;
|
|
+
|
|
+ if (expect->expectant->helper->timeout)
|
|
+ len = sprintf(buffer, "EXPECTING: %lu ",
|
|
+ timer_pending(&expect->timeout)
|
|
+ ? (expect->timeout.expires - jiffies)/HZ : 0);
|
|
+ else
|
|
+ len = sprintf(buffer, "EXPECTING: - ");
|
|
+ len += sprintf(buffer + len, "use=%u proto=%u ",
|
|
+ atomic_read(&expect->use), expect->tuple.dst.protonum);
|
|
+ len += print_tuple(buffer + len, &expect->tuple,
|
|
+ __ip6_ct_find_proto(expect->tuple.dst.protonum));
|
|
+ len += sprintf(buffer + len, "\n");
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static unsigned int
|
|
+print_conntrack(char *buffer, struct ip6_conntrack *conntrack)
|
|
+{
|
|
+ unsigned int len;
|
|
+ struct ip6_conntrack_protocol *proto
|
|
+ = __ip6_ct_find_proto(conntrack->tuplehash[IP6_CT_DIR_ORIGINAL]
|
|
+ .tuple.dst.protonum);
|
|
+
|
|
+ len = sprintf(buffer, "%-8s %u %lu ",
|
|
+ proto->name,
|
|
+ conntrack->tuplehash[IP6_CT_DIR_ORIGINAL]
|
|
+ .tuple.dst.protonum,
|
|
+ timer_pending(&conntrack->timeout)
|
|
+ ? (conntrack->timeout.expires - jiffies)/HZ : 0);
|
|
+
|
|
+ len += proto->print_conntrack(buffer + len, conntrack);
|
|
+ len += print_tuple(buffer + len,
|
|
+ &conntrack->tuplehash[IP6_CT_DIR_ORIGINAL].tuple,
|
|
+ proto);
|
|
+ if (!(test_bit(IP6S_SEEN_REPLY_BIT, &conntrack->status)))
|
|
+ len += sprintf(buffer + len, "[UNREPLIED] ");
|
|
+ len += print_tuple(buffer + len,
|
|
+ &conntrack->tuplehash[IP6_CT_DIR_REPLY].tuple,
|
|
+ proto);
|
|
+ if (test_bit(IP6S_ASSURED_BIT, &conntrack->status))
|
|
+ len += sprintf(buffer + len, "[ASSURED] ");
|
|
+ len += sprintf(buffer + len, "use=%u ",
|
|
+ atomic_read(&conntrack->ct_general.use));
|
|
+ len += sprintf(buffer + len, "\n");
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+/* Returns true when finished. */
|
|
+static inline int
|
|
+conntrack_iterate(const struct ip6_conntrack_tuple_hash *hash,
|
|
+ char *buffer, off_t offset, off_t *upto,
|
|
+ unsigned int *len, unsigned int maxlen)
|
|
+{
|
|
+ unsigned int newlen;
|
|
+ IP6_NF_ASSERT(hash->ctrack);
|
|
+
|
|
+ MUST_BE_READ_LOCKED(&ip6_conntrack_lock);
|
|
+
|
|
+ /* Only count originals */
|
|
+ if (DIRECTION(hash))
|
|
+ return 0;
|
|
+
|
|
+ if ((*upto)++ < offset)
|
|
+ return 0;
|
|
+
|
|
+ newlen = print_conntrack(buffer + *len, hash->ctrack);
|
|
+ if (*len + newlen > maxlen)
|
|
+ return 1;
|
|
+ else *len += newlen;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+list_conntracks(char *buffer, char **start, off_t offset, int length)
|
|
+{
|
|
+ unsigned int i;
|
|
+ unsigned int len = 0;
|
|
+ off_t upto = 0;
|
|
+ struct list_head *e;
|
|
+
|
|
+ READ_LOCK(&ip6_conntrack_lock);
|
|
+ /* Traverse hash; print originals then reply. */
|
|
+ for (i = 0; i < ip6_conntrack_htable_size; i++) {
|
|
+ if (LIST_FIND(&ip6_conntrack_hash[i], conntrack_iterate,
|
|
+ struct ip6_conntrack_tuple_hash *,
|
|
+ buffer, offset, &upto, &len, length))
|
|
+ goto finished;
|
|
+ }
|
|
+
|
|
+ /* Now iterate through expecteds. */
|
|
+ for (e = ip6_conntrack_expect_list.next;
|
|
+ e != &ip6_conntrack_expect_list; e = e->next) {
|
|
+ unsigned int last_len;
|
|
+ struct ip6_conntrack_expect *expect
|
|
+ = (struct ip6_conntrack_expect *)e;
|
|
+ if (upto++ < offset) continue;
|
|
+
|
|
+ last_len = len;
|
|
+ len += print_expect(buffer + len, expect);
|
|
+ if (len > length) {
|
|
+ len = last_len;
|
|
+ goto finished;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ finished:
|
|
+ READ_UNLOCK(&ip6_conntrack_lock);
|
|
+
|
|
+ /* `start' hack - see fs/proc/generic.c line ~165 */
|
|
+ *start = (char *)((unsigned int)upto - offset);
|
|
+ return len;
|
|
+}
|
|
+
|
|
+static unsigned int ip6_confirm(unsigned int hooknum,
|
|
+ struct sk_buff **pskb,
|
|
+ const struct net_device *in,
|
|
+ const struct net_device *out,
|
|
+ int (*okfn)(struct sk_buff *));
|
|
+static unsigned int ip6_conntrack_out(unsigned int hooknum,
|
|
+ struct sk_buff **pskb,
|
|
+ const struct net_device *in,
|
|
+ const struct net_device *out,
|
|
+ int (*okfn)(struct sk_buff *));
|
|
+static unsigned int ip6_conntrack_reasm(unsigned int hooknum,
|
|
+ struct sk_buff **pskb,
|
|
+ const struct net_device *in,
|
|
+ const struct net_device *out,
|
|
+ int (*okfn)(struct sk_buff *));
|
|
+static unsigned int ip6_conntrack_local(unsigned int hooknum,
|
|
+ struct sk_buff **pskb,
|
|
+ const struct net_device *in,
|
|
+ const struct net_device *out,
|
|
+ int (*okfn)(struct sk_buff *));
|
|
+
|
|
+/* Connection tracking may drop packets, but never alters them, so
|
|
+ make it the first hook. */
|
|
+static struct nf_hook_ops ip6_conntrack_in_ops = {
|
|
+ /* Don't forget to change .hook to "ip6_conntrack_input". - zak */
|
|
+ .hook = ip6_conntrack_reasm,
|
|
+ .owner = THIS_MODULE,
|
|
+ .pf = PF_INET6,
|
|
+ .hooknum = NF_IP6_PRE_ROUTING,
|
|
+ .priority = NF_IP6_PRI_CONNTRACK,
|
|
+};
|
|
+
|
|
+static struct nf_hook_ops ip6_conntrack_local_out_ops = {
|
|
+ .hook = ip6_conntrack_local,
|
|
+ .owner = THIS_MODULE,
|
|
+ .pf = PF_INET6,
|
|
+ .hooknum = NF_IP6_LOCAL_OUT,
|
|
+ .priority = NF_IP6_PRI_CONNTRACK,
|
|
+};
|
|
+
|
|
+/* Refragmenter; last chance. */
|
|
+static struct nf_hook_ops ip6_conntrack_out_ops = {
|
|
+ .hook = ip6_conntrack_out,
|
|
+ .owner = THIS_MODULE,
|
|
+ .pf = PF_INET6,
|
|
+ .hooknum = NF_IP6_POST_ROUTING,
|
|
+ .priority = NF_IP6_PRI_LAST,
|
|
+};
|
|
+
|
|
+static struct nf_hook_ops ip6_conntrack_local_in_ops = {
|
|
+ .hook = ip6_confirm,
|
|
+ .owner = THIS_MODULE,
|
|
+ .pf = PF_INET6,
|
|
+ .hooknum = NF_IP6_LOCAL_IN,
|
|
+ .priority = NF_IP6_PRI_LAST-1,
|
|
+};
|
|
+
|
|
+static unsigned int ip6_confirm(unsigned int hooknum,
|
|
+ struct sk_buff **pskb,
|
|
+ const struct net_device *in,
|
|
+ const struct net_device *out,
|
|
+ int (*okfn)(struct sk_buff *))
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = ip6_conntrack_confirm(*pskb);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static unsigned int ip6_conntrack_out(unsigned int hooknum,
|
|
+ struct sk_buff **pskb,
|
|
+ const struct net_device *in,
|
|
+ const struct net_device *out,
|
|
+ int (*okfn)(struct sk_buff *))
|
|
+{
|
|
+
|
|
+ if (ip6_conntrack_confirm(*pskb) != NF_ACCEPT)
|
|
+ return NF_DROP;
|
|
+
|
|
+ return NF_ACCEPT;
|
|
+}
|
|
+
|
|
+static unsigned int ip6_conntrack_reasm(unsigned int hooknum,
|
|
+ struct sk_buff **pskb,
|
|
+ const struct net_device *in,
|
|
+ const struct net_device *out,
|
|
+ int (*okfn)(struct sk_buff *))
|
|
+{
|
|
+ struct sk_buff *skb = *pskb;
|
|
+ struct sk_buff **rsmd_pskb = &skb;
|
|
+ int fragd = 0;
|
|
+ int ret;
|
|
+
|
|
+ skb->nfcache |= NFC_UNKNOWN;
|
|
+
|
|
+ /*
|
|
+ * Previously seen (loopback)? Ignore. Do this before
|
|
+ * fragment check.
|
|
+ */
|
|
+ if (skb->nfct) {
|
|
+ DEBUGP("previously seen\n");
|
|
+ return NF_ACCEPT;
|
|
+ }
|
|
+
|
|
+ skb = ip6_ct_gather_frags(skb);
|
|
+
|
|
+ /* queued */
|
|
+ if (skb == NULL)
|
|
+ return NF_STOLEN;
|
|
+
|
|
+ if (skb != (*pskb))
|
|
+ fragd = 1;
|
|
+
|
|
+ ret = ip6_conntrack_in(hooknum, rsmd_pskb, in, out, okfn);
|
|
+
|
|
+ if (!fragd)
|
|
+ return ret;
|
|
+
|
|
+ if (ret == NF_DROP) {
|
|
+ ip6_ct_kfree_frags(skb);
|
|
+ }else{
|
|
+ struct nf_info info;
|
|
+
|
|
+ info.pf = PF_INET6;
|
|
+ info.hook = hooknum;
|
|
+ info.indev = in;
|
|
+ info.outdev = out;
|
|
+ info.okfn = okfn;
|
|
+ switch (hooknum) {
|
|
+ case NF_IP6_PRE_ROUTING:
|
|
+ info.elem = &ip6_conntrack_in_ops;
|
|
+ break;
|
|
+ case NF_IP6_LOCAL_OUT:
|
|
+ info.elem = &ip6_conntrack_local_out_ops;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (ip6_ct_output_frags(skb, &info) <0)
|
|
+ DEBUGP("Can't output fragments\n");
|
|
+
|
|
+ }
|
|
+
|
|
+ return NF_STOLEN;
|
|
+}
|
|
+
|
|
+static unsigned int ip6_conntrack_local(unsigned int hooknum,
|
|
+ struct sk_buff **pskb,
|
|
+ const struct net_device *in,
|
|
+ const struct net_device *out,
|
|
+ int (*okfn)(struct sk_buff *))
|
|
+{
|
|
+ unsigned int ret;
|
|
+
|
|
+ /* root is playing with raw sockets. */
|
|
+ if ((*pskb)->len < sizeof(struct ipv6hdr)) {
|
|
+ if (net_ratelimit())
|
|
+ printk("ip6t_hook: IPv6 header is too short.\n");
|
|
+ return NF_ACCEPT;
|
|
+ }
|
|
+
|
|
+ ret = ip6_conntrack_reasm(hooknum, pskb, in, out, okfn);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int init_or_cleanup(int init)
|
|
+{
|
|
+ struct proc_dir_entry *proc;
|
|
+ int ret = 0;
|
|
+
|
|
+ if (!init) goto cleanup;
|
|
+
|
|
+ ret = ip6_ct_frags_init();
|
|
+ if (ret < 0)
|
|
+ goto cleanup_reasm;
|
|
+
|
|
+ ret = ip6_conntrack_init();
|
|
+ if (ret < 0)
|
|
+ goto cleanup_nothing;
|
|
+
|
|
+ proc = proc_net_create("ip6_conntrack",0,list_conntracks);
|
|
+ if (!proc) goto cleanup_init;
|
|
+ proc->owner = THIS_MODULE;
|
|
+
|
|
+ ret = nf_register_hook(&ip6_conntrack_in_ops);
|
|
+ if (ret < 0) {
|
|
+ printk("ip6_conntrack: can't register pre-routing hook.\n");
|
|
+ goto cleanup_proc;
|
|
+ }
|
|
+ ret = nf_register_hook(&ip6_conntrack_local_out_ops);
|
|
+ if (ret < 0) {
|
|
+ printk("ip6_conntrack: can't register local out hook.\n");
|
|
+ goto cleanup_inops;
|
|
+ }
|
|
+ ret = nf_register_hook(&ip6_conntrack_out_ops);
|
|
+ if (ret < 0) {
|
|
+ printk("ip6_conntrack: can't register post-routing hook.\n");
|
|
+ goto cleanup_inandlocalops;
|
|
+ }
|
|
+ ret = nf_register_hook(&ip6_conntrack_local_in_ops);
|
|
+ if (ret < 0) {
|
|
+ printk("ip6_conntrack: can't register local in hook.\n");
|
|
+ goto cleanup_inoutandlocalops;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+
|
|
+ cleanup:
|
|
+ nf_unregister_hook(&ip6_conntrack_local_in_ops);
|
|
+ cleanup_inoutandlocalops:
|
|
+ nf_unregister_hook(&ip6_conntrack_out_ops);
|
|
+ cleanup_inandlocalops:
|
|
+ nf_unregister_hook(&ip6_conntrack_local_out_ops);
|
|
+ cleanup_inops:
|
|
+ nf_unregister_hook(&ip6_conntrack_in_ops);
|
|
+ cleanup_proc:
|
|
+ proc_net_remove("ip6_conntrack");
|
|
+ cleanup_init:
|
|
+ ip6_conntrack_cleanup();
|
|
+ cleanup_reasm:
|
|
+ ip6_ct_frags_cleanup();
|
|
+ cleanup_nothing:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/* FIXME: Allow NULL functions and sub in pointers to generic for
|
|
+ them. --RR */
|
|
+int ip6_conntrack_protocol_register(struct ip6_conntrack_protocol *proto)
|
|
+{
|
|
+ int ret = 0;
|
|
+ struct list_head *i;
|
|
+
|
|
+ WRITE_LOCK(&ip6_conntrack_lock);
|
|
+ for (i = ip6_protocol_list.next; i != &ip6_protocol_list; i = i->next) {
|
|
+ if (((struct ip6_conntrack_protocol *)i)->proto
|
|
+ == proto->proto) {
|
|
+ ret = -EBUSY;
|
|
+ goto out;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ list_prepend(&ip6_protocol_list, proto);
|
|
+
|
|
+ out:
|
|
+ WRITE_UNLOCK(&ip6_conntrack_lock);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void ip6_conntrack_protocol_unregister(struct ip6_conntrack_protocol *proto)
|
|
+{
|
|
+ WRITE_LOCK(&ip6_conntrack_lock);
|
|
+
|
|
+ /* ip_ct_find_proto() returns proto_generic in case there is no protocol
|
|
+ * helper. So this should be enough - HW */
|
|
+ LIST_DELETE(&ip6_protocol_list, proto);
|
|
+ WRITE_UNLOCK(&ip6_conntrack_lock);
|
|
+
|
|
+ /* Somebody could be still looking at the proto in bh. */
|
|
+ synchronize_net();
|
|
+
|
|
+ /* Remove all contrack entries for this protocol */
|
|
+ ip6_ct_selective_cleanup(kill_proto, &proto->proto);
|
|
+}
|
|
+
|
|
+static int __init init(void)
|
|
+{
|
|
+ return init_or_cleanup(1);
|
|
+}
|
|
+
|
|
+static void __exit fini(void)
|
|
+{
|
|
+ init_or_cleanup(0);
|
|
+}
|
|
+
|
|
+module_init(init);
|
|
+module_exit(fini);
|
|
+
|
|
+/* Some modules need us, but don't depend directly on any symbol.
|
|
+ They should call this. */
|
|
+void need_ip6_conntrack(void)
|
|
+{
|
|
+}
|
|
+
|
|
+EXPORT_SYMBOL(ip6_conntrack_protocol_register);
|
|
+EXPORT_SYMBOL(ip6_conntrack_protocol_unregister);
|
|
+EXPORT_SYMBOL(ip6_invert_tuplepr);
|
|
+EXPORT_SYMBOL(ip6_conntrack_alter_reply);
|
|
+EXPORT_SYMBOL(ip6_conntrack_destroyed);
|
|
+EXPORT_SYMBOL(ip6_conntrack_get);
|
|
+EXPORT_SYMBOL(need_ip6_conntrack);
|
|
+EXPORT_SYMBOL(ip6_conntrack_helper_register);
|
|
+EXPORT_SYMBOL(ip6_conntrack_helper_unregister);
|
|
+EXPORT_SYMBOL(ip6_ct_selective_cleanup);
|
|
+EXPORT_SYMBOL(ip6_ct_refresh);
|
|
+EXPORT_SYMBOL(ip6_ct_find_proto);
|
|
+EXPORT_SYMBOL(__ip6_ct_find_proto);
|
|
+EXPORT_SYMBOL(ip6_ct_find_helper);
|
|
+EXPORT_SYMBOL(ip6_conntrack_expect_related);
|
|
+EXPORT_SYMBOL(ip6_conntrack_unexpect_related);
|
|
+EXPORT_SYMBOL_GPL(ip6_conntrack_expect_find_get);
|
|
+EXPORT_SYMBOL_GPL(ip6_conntrack_expect_put);
|
|
+EXPORT_SYMBOL(ip6_conntrack_tuple_taken);
|
|
+EXPORT_SYMBOL(ip6_conntrack_htable_size);
|
|
+EXPORT_SYMBOL(ip6_conntrack_expect_list);
|
|
+EXPORT_SYMBOL(ip6_conntrack_lock);
|
|
+EXPORT_SYMBOL_GPL(ip6_conntrack_find_get);
|
|
+EXPORT_SYMBOL_GPL(ip6_conntrack_put);
|
|
diff -urN linux-2.6.11/net/ipv6/netfilter/ip6t_REJECT.c x1/net/ipv6/netfilter/ip6t_REJECT.c
|
|
--- linux-2.6.11/net/ipv6/netfilter/ip6t_REJECT.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/net/ipv6/netfilter/ip6t_REJECT.c 2004-11-25 15:47:19.000000000 +0100
|
|
@@ -0,0 +1,453 @@
|
|
+/*
|
|
+ * IP6 tables REJECT target module
|
|
+ * Linux INET6 implementation
|
|
+ *
|
|
+ * Copyright (C)2003 USAGI/WIDE Project
|
|
+ *
|
|
+ * Authors:
|
|
+ * Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
|
|
+ *
|
|
+ * Based on net/ipv4/netfilter/ipt_REJECT.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.
|
|
+ */
|
|
+
|
|
+/* This module works well with IPv6 Connection Tracking. - kozakai */
|
|
+
|
|
+#include <linux/config.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/skbuff.h>
|
|
+#include <linux/icmpv6.h>
|
|
+#include <net/ipv6.h>
|
|
+#include <net/tcp.h>
|
|
+#include <net/icmp.h>
|
|
+#include <net/ip6_fib.h>
|
|
+#include <net/ip6_route.h>
|
|
+#include <net/flow.h>
|
|
+#include <linux/netfilter_ipv6/ip6_tables.h>
|
|
+#include <linux/netfilter_ipv6/ip6t_REJECT.h>
|
|
+
|
|
+MODULE_AUTHOR("Yasuyuki KOZAKAI <yasuyuki.kozakai@toshiba.co.jp>");
|
|
+MODULE_DESCRIPTION("IP6 tables REJECT target module");
|
|
+MODULE_LICENSE("GPL");
|
|
+
|
|
+#if 0
|
|
+#define DEBUGP printk
|
|
+#else
|
|
+#define DEBUGP(format, args...)
|
|
+#endif
|
|
+
|
|
+static void connection_attach(struct sk_buff *new_skb, struct sk_buff *skb)
|
|
+{
|
|
+ void (*attach)(struct sk_buff *, struct sk_buff *);
|
|
+ if (skb->nfct && (attach = ip6_ct_attach) != NULL) {
|
|
+ mb();
|
|
+ attach(new_skb, skb);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int maybe_reroute(struct sk_buff *skb)
|
|
+{
|
|
+ if (skb->nfcache & NFC_ALTERED){
|
|
+ if (ip6_route_me_harder(skb) != 0){
|
|
+ kfree_skb(skb);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return dst_output(skb);
|
|
+}
|
|
+
|
|
+/* Send RST reply */
|
|
+static void send_reset(struct sk_buff *oldskb)
|
|
+{
|
|
+ struct sk_buff *nskb;
|
|
+ struct tcphdr otcph, *tcph;
|
|
+ unsigned int otcplen, tcphoff, hh_len;
|
|
+ int needs_ack;
|
|
+ struct ipv6hdr *oip6h = oldskb->nh.ipv6h, *ip6h;
|
|
+ struct dst_entry *dst = NULL;
|
|
+ u8 proto = oip6h->nexthdr;
|
|
+ struct flowi fl;
|
|
+ int err;
|
|
+
|
|
+ if ((!(ipv6_addr_type(&oip6h->saddr) & IPV6_ADDR_UNICAST)) ||
|
|
+ (!(ipv6_addr_type(&oip6h->daddr) & IPV6_ADDR_UNICAST))) {
|
|
+ DEBUGP("ip6t_REJECT: addr is not unicast.\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ tcphoff = ipv6_skip_exthdr(oldskb, ((u8*)(oip6h+1) - oldskb->data),
|
|
+ &proto, oldskb->len - ((u8*)(oip6h+1)
|
|
+ - oldskb->data));
|
|
+
|
|
+ if ((tcphoff < 0) || (tcphoff > oldskb->len)) {
|
|
+ DEBUGP("ip6t_REJECT: Can't get TCP header.\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ otcplen = oldskb->len - tcphoff;
|
|
+
|
|
+ /* IP header checks: fragment, too short. */
|
|
+ if ((proto != IPPROTO_TCP) || (otcplen < sizeof(struct tcphdr))) {
|
|
+ DEBUGP("ip6t_REJECT: proto(%d) != IPPROTO_TCP, or too short. otcplen = %d\n",
|
|
+ proto, otcplen);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (skb_copy_bits(oldskb, tcphoff, &otcph, sizeof(struct tcphdr))) {
|
|
+ if (net_ratelimit())
|
|
+ printk("ip6t_REJECT: Can't copy tcp header\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* No RST for RST. */
|
|
+ if (otcph.rst) {
|
|
+ DEBUGP("ip6t_REJECT: RST is set\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* Check checksum. */
|
|
+ if (csum_ipv6_magic(&oip6h->saddr, &oip6h->daddr, otcplen, IPPROTO_TCP,
|
|
+ skb_checksum(oldskb, tcphoff, otcplen, 0))) {
|
|
+ DEBUGP("ip6t_REJECT: TCP checksum is invalid\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ memset(&fl, 0, sizeof(fl));
|
|
+ fl.proto = IPPROTO_TCP;
|
|
+ ipv6_addr_copy(&fl.fl6_src, &oip6h->daddr);
|
|
+ ipv6_addr_copy(&fl.fl6_dst, &oip6h->saddr);
|
|
+ fl.fl_ip_sport = otcph.dest;
|
|
+ fl.fl_ip_dport = otcph.source;
|
|
+ err = ip6_dst_lookup(NULL, &dst, &fl);
|
|
+ if (err) {
|
|
+ if (net_ratelimit())
|
|
+ printk("ip6t_REJECT: can't find dst. err = %d\n", err);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ hh_len = (dst->dev->hard_header_len + 15)&~15;
|
|
+ nskb = alloc_skb(hh_len + 15 + dst->header_len + sizeof(struct ipv6hdr)
|
|
+ + sizeof(struct tcphdr) + dst->trailer_len,
|
|
+ GFP_ATOMIC);
|
|
+
|
|
+ if (!nskb) {
|
|
+ if (net_ratelimit())
|
|
+ printk("ip6t_REJECT: Can't alloc skb\n");
|
|
+ dst_release(dst);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ nskb->dst = dst;
|
|
+ dst_hold(dst);
|
|
+
|
|
+ skb_reserve(nskb, hh_len + dst->header_len);
|
|
+
|
|
+ ip6h = nskb->nh.ipv6h = (struct ipv6hdr *)
|
|
+ skb_put(nskb, sizeof(struct ipv6hdr));
|
|
+ ip6h->version = 6;
|
|
+ ip6h->hop_limit = dst_metric(dst, RTAX_HOPLIMIT);
|
|
+ ip6h->nexthdr = IPPROTO_TCP;
|
|
+ ip6h->payload_len = htons(sizeof(struct tcphdr));
|
|
+ ipv6_addr_copy(&ip6h->saddr, &oip6h->daddr);
|
|
+ ipv6_addr_copy(&ip6h->daddr, &oip6h->saddr);
|
|
+
|
|
+ tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr));
|
|
+ /* Truncate to length (no data) */
|
|
+ tcph->doff = sizeof(struct tcphdr)/4;
|
|
+ tcph->source = otcph.dest;
|
|
+ tcph->dest = otcph.source;
|
|
+
|
|
+ if (otcph.ack) {
|
|
+ needs_ack = 0;
|
|
+ tcph->seq = otcph.ack_seq;
|
|
+ tcph->ack_seq = 0;
|
|
+ } else {
|
|
+ needs_ack = 1;
|
|
+ tcph->ack_seq = htonl(ntohl(otcph.seq) + otcph.syn + otcph.fin
|
|
+ + otcplen - (otcph.doff<<2));
|
|
+ tcph->seq = 0;
|
|
+ }
|
|
+
|
|
+ /* Reset flags */
|
|
+ ((u_int8_t *)tcph)[13] = 0;
|
|
+ tcph->rst = 1;
|
|
+ tcph->ack = needs_ack;
|
|
+ tcph->window = 0;
|
|
+ tcph->urg_ptr = 0;
|
|
+ tcph->check = 0;
|
|
+
|
|
+ /* Adjust TCP checksum */
|
|
+ tcph->check = csum_ipv6_magic(&nskb->nh.ipv6h->saddr,
|
|
+ &nskb->nh.ipv6h->daddr,
|
|
+ sizeof(struct tcphdr), IPPROTO_TCP,
|
|
+ csum_partial((char *)tcph,
|
|
+ sizeof(struct tcphdr), 0));
|
|
+
|
|
+ connection_attach(nskb, oldskb);
|
|
+
|
|
+ NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
|
|
+ maybe_reroute);
|
|
+
|
|
+ dst_release(dst);
|
|
+}
|
|
+
|
|
+static void send_unreach(struct sk_buff *skb_in, unsigned char code)
|
|
+{
|
|
+ struct ipv6hdr *ip6h, *hdr = skb_in->nh.ipv6h;
|
|
+ struct icmp6hdr *icmp6h;
|
|
+ struct dst_entry *dst = NULL;
|
|
+ struct rt6_info *rt;
|
|
+ int tmo;
|
|
+ __u32 csum;
|
|
+ unsigned int len, datalen, hh_len;
|
|
+ int saddr_type, daddr_type;
|
|
+ unsigned int ptr, ip6off;
|
|
+ u8 proto;
|
|
+ struct flowi fl;
|
|
+ struct sk_buff *nskb;
|
|
+ char *data;
|
|
+
|
|
+ saddr_type = ipv6_addr_type(&hdr->saddr);
|
|
+ daddr_type = ipv6_addr_type(&hdr->daddr);
|
|
+
|
|
+ if ((!(saddr_type & IPV6_ADDR_UNICAST)) ||
|
|
+ (!(daddr_type & IPV6_ADDR_UNICAST))) {
|
|
+ DEBUGP("ip6t_REJECT: addr is not unicast.\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ ip6off = skb_in->nh.raw - skb_in->data;
|
|
+ proto = hdr->nexthdr;
|
|
+ ptr = ipv6_skip_exthdr(skb_in, ip6off + sizeof(struct ipv6hdr), &proto,
|
|
+ skb_in->len - ip6off);
|
|
+
|
|
+ if ((ptr < 0) || (ptr > skb_in->len)) {
|
|
+ ptr = ip6off + sizeof(struct ipv6hdr);
|
|
+ proto = hdr->nexthdr;
|
|
+ } else if (proto == IPPROTO_ICMPV6) {
|
|
+ u8 type;
|
|
+
|
|
+ if (skb_copy_bits(skb_in, ptr + offsetof(struct icmp6hdr,
|
|
+ icmp6_type), &type, 1)) {
|
|
+ DEBUGP("ip6t_REJECT: Can't get ICMPv6 type\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!(type & ICMPV6_INFOMSG_MASK)) {
|
|
+ DEBUGP("ip6t_REJECT: no reply to icmp error\n");
|
|
+ return;
|
|
+ }
|
|
+ } else if (proto == IPPROTO_UDP) {
|
|
+ int plen = skb_in->len - (ptr - ip6off);
|
|
+ uint16_t check;
|
|
+
|
|
+ if (plen < sizeof(struct udphdr)) {
|
|
+ DEBUGP("ip6t_REJECT: too short\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (skb_copy_bits(skb_in, ptr + offsetof(struct udphdr, check),
|
|
+ &check, 2)) {
|
|
+ if (net_ratelimit())
|
|
+ printk("ip6t_REJECT: can't get copy from skb");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (check &&
|
|
+ csum_ipv6_magic(&hdr->saddr, &hdr->daddr, plen,
|
|
+ IPPROTO_UDP,
|
|
+ skb_checksum(skb_in, ptr, plen, 0))) {
|
|
+ DEBUGP("ip6t_REJECT: UDP checksum is invalid.\n");
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ memset(&fl, 0, sizeof(fl));
|
|
+ fl.proto = IPPROTO_ICMPV6;
|
|
+ ipv6_addr_copy(&fl.fl6_src, &hdr->daddr);
|
|
+ ipv6_addr_copy(&fl.fl6_dst, &hdr->saddr);
|
|
+ fl.fl_icmp_type = ICMPV6_DEST_UNREACH;
|
|
+ fl.fl_icmp_code = code;
|
|
+
|
|
+ if (ip6_dst_lookup(NULL, &dst, &fl)) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ rt = (struct rt6_info *)dst;
|
|
+ tmo = 1*HZ;
|
|
+
|
|
+ if (rt->rt6i_dst.plen < 128)
|
|
+ tmo >>= ((128 - rt->rt6i_dst.plen)>>5);
|
|
+
|
|
+ if (!xrlim_allow(dst, tmo)) {
|
|
+ if (net_ratelimit())
|
|
+ printk("ip6t_REJECT: rate limitted\n");
|
|
+ goto dst_release_out;
|
|
+ }
|
|
+
|
|
+ len = skb_in->len + sizeof(struct ipv6hdr) + sizeof(struct icmp6hdr);
|
|
+
|
|
+ if (len > dst_pmtu(dst))
|
|
+ len = dst_pmtu(dst);
|
|
+ if (len > IPV6_MIN_MTU)
|
|
+ len = IPV6_MIN_MTU;
|
|
+
|
|
+ datalen = len - sizeof(struct ipv6hdr) - sizeof(struct icmp6hdr);
|
|
+ hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
|
|
+
|
|
+ nskb = alloc_skb(hh_len + 15 + dst->header_len + dst->trailer_len + len,
|
|
+ GFP_ATOMIC);
|
|
+
|
|
+ if (!nskb) {
|
|
+ if (net_ratelimit())
|
|
+ printk("ip6t_REJECT: can't alloc skb\n");
|
|
+ goto dst_release_out;
|
|
+ }
|
|
+
|
|
+ nskb->priority = 0;
|
|
+ nskb->dst = dst;
|
|
+ dst_hold(dst);
|
|
+
|
|
+ skb_reserve(nskb, hh_len + dst->header_len);
|
|
+
|
|
+ ip6h = nskb->nh.ipv6h = (struct ipv6hdr *)
|
|
+ skb_put(nskb, sizeof(struct ipv6hdr));
|
|
+ ip6h->version = 6;
|
|
+ ip6h->hop_limit = dst_metric(dst, RTAX_HOPLIMIT);
|
|
+ ip6h->nexthdr = IPPROTO_ICMPV6;
|
|
+ ip6h->payload_len = htons(datalen + sizeof(struct icmp6hdr));
|
|
+ ipv6_addr_copy(&ip6h->saddr, &hdr->daddr);
|
|
+ ipv6_addr_copy(&ip6h->daddr, &hdr->saddr);
|
|
+
|
|
+ icmp6h = (struct icmp6hdr *) skb_put(nskb, sizeof(struct icmp6hdr));
|
|
+ icmp6h->icmp6_type = ICMPV6_DEST_UNREACH;
|
|
+ icmp6h->icmp6_code = code;
|
|
+ icmp6h->icmp6_cksum = 0;
|
|
+
|
|
+ data = skb_put(nskb, datalen);
|
|
+
|
|
+ csum = csum_partial((unsigned char *)icmp6h, sizeof(struct icmp6hdr), 0);
|
|
+ csum = skb_copy_and_csum_bits(skb_in, ip6off, data, datalen, csum);
|
|
+ icmp6h->icmp6_cksum = csum_ipv6_magic(&hdr->saddr, &hdr->daddr,
|
|
+ datalen + sizeof(struct icmp6hdr),
|
|
+ IPPROTO_ICMPV6, csum);
|
|
+
|
|
+ connection_attach(nskb, skb_in);
|
|
+ NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
|
|
+ maybe_reroute);
|
|
+
|
|
+dst_release_out:
|
|
+ dst_release(dst);
|
|
+}
|
|
+
|
|
+static unsigned int reject6_target(struct sk_buff **pskb,
|
|
+ const struct net_device *in,
|
|
+ const struct net_device *out,
|
|
+ unsigned int hooknum,
|
|
+ const void *targinfo,
|
|
+ void *userinfo)
|
|
+{
|
|
+ const struct ip6t_reject_info *reject = targinfo;
|
|
+
|
|
+ DEBUGP(KERN_DEBUG "%s: medium point\n", __FUNCTION__);
|
|
+ /* WARNING: This code causes reentry within ip6tables.
|
|
+ This means that the ip6tables jump stack is now crap. We
|
|
+ must return an absolute verdict. --RR */
|
|
+ switch (reject->with) {
|
|
+ case IP6T_ICMP6_NO_ROUTE:
|
|
+ send_unreach(*pskb, ICMPV6_NOROUTE);
|
|
+ break;
|
|
+ case IP6T_ICMP6_ADM_PROHIBITED:
|
|
+ send_unreach(*pskb, ICMPV6_ADM_PROHIBITED);
|
|
+ break;
|
|
+ case IP6T_ICMP6_NOT_NEIGHBOUR:
|
|
+ send_unreach(*pskb, ICMPV6_NOT_NEIGHBOUR);
|
|
+ break;
|
|
+ case IP6T_ICMP6_ADDR_UNREACH:
|
|
+ send_unreach(*pskb, ICMPV6_ADDR_UNREACH);
|
|
+ break;
|
|
+ case IP6T_ICMP6_PORT_UNREACH:
|
|
+ send_unreach(*pskb, ICMPV6_PORT_UNREACH);
|
|
+ break;
|
|
+ case IP6T_ICMP6_ECHOREPLY:
|
|
+ /* Do nothing */
|
|
+ break;
|
|
+ case IP6T_TCP_RESET:
|
|
+ send_reset(*pskb);
|
|
+ break;
|
|
+ default:
|
|
+ if (net_ratelimit())
|
|
+ printk(KERN_WARNING "ip6t_REJECT: case %u not handled yet\n", reject->with);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return NF_DROP;
|
|
+}
|
|
+
|
|
+static int check(const char *tablename,
|
|
+ const struct ip6t_entry *e,
|
|
+ void *targinfo,
|
|
+ unsigned int targinfosize,
|
|
+ unsigned int hook_mask)
|
|
+{
|
|
+ const struct ip6t_reject_info *rejinfo = targinfo;
|
|
+
|
|
+ if (targinfosize != IP6T_ALIGN(sizeof(struct ip6t_reject_info))) {
|
|
+ DEBUGP("ip6t_REJECT: targinfosize %u != 0\n", targinfosize);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ /* Only allow these for packet filtering. */
|
|
+ if (strcmp(tablename, "filter") != 0) {
|
|
+ DEBUGP("ip6t_REJECT: bad table `%s'.\n", tablename);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if ((hook_mask & ~((1 << NF_IP6_LOCAL_IN)
|
|
+ | (1 << NF_IP6_FORWARD)
|
|
+ | (1 << NF_IP6_LOCAL_OUT))) != 0) {
|
|
+ DEBUGP("ip6t_REJECT: bad hook mask %X\n", hook_mask);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (rejinfo->with == IP6T_ICMP6_ECHOREPLY) {
|
|
+ printk("ip6t_REJECT: ECHOREPLY is not supported.\n");
|
|
+ return 0;
|
|
+ } else if (rejinfo->with == IP6T_TCP_RESET) {
|
|
+ /* Must specify that it's a TCP packet */
|
|
+ if (e->ipv6.proto != IPPROTO_TCP
|
|
+ || (e->ipv6.invflags & IP6T_INV_PROTO)) {
|
|
+ DEBUGP("ip6t_REJECT: TCP_RESET illegal for non-tcp\n");
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static struct ip6t_target ip6t_reject_reg = {
|
|
+ .name = "REJECT",
|
|
+ .target = reject6_target,
|
|
+ .checkentry = check,
|
|
+ .me = THIS_MODULE
|
|
+};
|
|
+
|
|
+static int __init init(void)
|
|
+{
|
|
+ if (ip6t_register_target(&ip6t_reject_reg))
|
|
+ return -EINVAL;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void __exit fini(void)
|
|
+{
|
|
+ ip6t_unregister_target(&ip6t_reject_reg);
|
|
+}
|
|
+
|
|
+module_init(init);
|
|
+module_exit(fini);
|
|
diff -urN linux-2.6.11/net/ipv6/netfilter/ip6t_state.c x1/net/ipv6/netfilter/ip6t_state.c
|
|
--- linux-2.6.11/net/ipv6/netfilter/ip6t_state.c 1970-01-01 01:00:00.000000000 +0100
|
|
+++ x1/net/ipv6/netfilter/ip6t_state.c 2004-11-25 15:47:19.000000000 +0100
|
|
@@ -0,0 +1,79 @@
|
|
+/*
|
|
+ * Matching connection tracking information
|
|
+ * Linux INET6 implementation
|
|
+ *
|
|
+ * Copyright (C)2003 USAGI/WIDE Project
|
|
+ *
|
|
+ * Authors:
|
|
+ * Yasuyuki Kozakai <yasuyuki.kozakai@toshiba.co.jp>
|
|
+ *
|
|
+ * Based on: net/ipv4/netfilter/ip6t_state.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.
|
|
+ */
|
|
+/* Kernel module to match connection tracking information.
|
|
+ * GPL (C) 1999 Rusty Russell (rusty@rustcorp.com.au).
|
|
+ */
|
|
+#include <linux/module.h>
|
|
+#include <linux/skbuff.h>
|
|
+#include <linux/netfilter_ipv6/ip6_conntrack.h>
|
|
+#include <linux/netfilter_ipv6/ip6_tables.h>
|
|
+#include <linux/netfilter_ipv6/ip6t_state.h>
|
|
+
|
|
+static int
|
|
+match(const struct sk_buff *skb,
|
|
+ const struct net_device *in,
|
|
+ const struct net_device *out,
|
|
+ const void *matchinfo,
|
|
+ int offset,
|
|
+ unsigned int protoff,
|
|
+ int *hotdrop)
|
|
+{
|
|
+ const struct ip6t_state_info *sinfo = matchinfo;
|
|
+ enum ip6_conntrack_info ctinfo;
|
|
+ unsigned int statebit;
|
|
+
|
|
+ if (!ip6_conntrack_get(skb, &ctinfo))
|
|
+ statebit = IP6T_STATE_INVALID;
|
|
+ else
|
|
+ statebit = IP6T_STATE_BIT(ctinfo);
|
|
+
|
|
+ return (sinfo->statemask & statebit);
|
|
+}
|
|
+
|
|
+static int check(const char *tablename,
|
|
+ const struct ip6t_ip6 *ip,
|
|
+ void *matchinfo,
|
|
+ unsigned int matchsize,
|
|
+ unsigned int hook_mask)
|
|
+{
|
|
+ if (matchsize != IP6T_ALIGN(sizeof(struct ip6t_state_info)))
|
|
+ return 0;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static struct ip6t_match state_match = {
|
|
+ .name = "state",
|
|
+ .match = &match,
|
|
+ .checkentry = &check,
|
|
+ .me = THIS_MODULE,
|
|
+};
|
|
+
|
|
+static int __init init(void)
|
|
+{
|
|
+ need_ip6_conntrack();
|
|
+ return ip6t_register_match(&state_match);
|
|
+}
|
|
+
|
|
+static void __exit fini(void)
|
|
+{
|
|
+ ip6t_unregister_match(&state_match);
|
|
+}
|
|
+
|
|
+module_init(init);
|
|
+module_exit(fini);
|
|
+MODULE_LICENSE("GPL");
|
|
diff -urN linux-2.6.11/net/ipv6/proc.c x1/net/ipv6/proc.c
|
|
--- linux-2.6.11/net/ipv6/proc.c 2005-03-02 08:38:07.000000000 +0100
|
|
+++ x1/net/ipv6/proc.c 2005-03-02 17:30:59.000000000 +0100
|
|
@@ -164,7 +164,13 @@
|
|
|
|
if (idev) {
|
|
seq_printf(seq, "%-32s\t%u\n", "ifIndex", idev->dev->ifindex);
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ snmp6_seq_show_item(seq, (void **)idev->stats.ipv6_statistics, snmp6_ipstats_list);
|
|
+#endif
|
|
snmp6_seq_show_item(seq, (void **)idev->stats.icmpv6, snmp6_icmp6_list);
|
|
+#if 0
|
|
+ snmp6_seq_show_item(seq, (void **)idev->stats.udp_stats_in6, snmp6_udp6_list);
|
|
+#endif
|
|
} else {
|
|
snmp6_seq_show_item(seq, (void **)ipv6_statistics, snmp6_ipstats_list);
|
|
snmp6_seq_show_item(seq, (void **)icmpv6_statistics, snmp6_icmp6_list);
|
|
@@ -284,19 +290,45 @@
|
|
if (!idev || !idev->dev)
|
|
return -EINVAL;
|
|
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ if (snmp6_mib_init((void **)idev->stats.ipv6_statistics, sizeof(struct ipstats_mib),
|
|
+ __alignof__(struct ipstats_mib)) < 0)
|
|
+ goto err_ip;
|
|
+#endif
|
|
+
|
|
if (snmp6_mib_init((void **)idev->stats.icmpv6, sizeof(struct icmpv6_mib),
|
|
__alignof__(struct icmpv6_mib)) < 0)
|
|
goto err_icmp;
|
|
|
|
+#if 0
|
|
+ if (snmp6_mib_init((void **)idev->stats.udp_stats_in6, sizeof(struct udp_mib),
|
|
+ __alignof__(struct udp_mib)) < 0)
|
|
+ goto err_udp;
|
|
+#endif
|
|
+
|
|
return 0;
|
|
|
|
+#if 0
|
|
+err_udp:
|
|
+ snmp6_mib_free((void **)idev->stats.icmpv6);
|
|
+#endif
|
|
err_icmp:
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ snmp6_mib_free((void **)idev->stats.ipv6_statistics);
|
|
+err_ip:
|
|
+#endif
|
|
return err;
|
|
}
|
|
|
|
int snmp6_free_dev(struct inet6_dev *idev)
|
|
{
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ snmp6_mib_free((void **)idev->stats.ipv6_statistics);
|
|
+#endif
|
|
snmp6_mib_free((void **)idev->stats.icmpv6);
|
|
+#if 0
|
|
+ snmp6_mib_free((void **)idev->stats.udp_stats_in6);
|
|
+#endif
|
|
return 0;
|
|
}
|
|
|
|
diff -urN linux-2.6.11/net/ipv6/raw.c x1/net/ipv6/raw.c
|
|
--- linux-2.6.11/net/ipv6/raw.c 2005-03-02 08:38:07.000000000 +0100
|
|
+++ x1/net/ipv6/raw.c 2005-02-09 16:31:39.000000000 +0100
|
|
@@ -13,6 +13,10 @@
|
|
* Hideaki YOSHIFUJI : sin6_scope_id support
|
|
* YOSHIFUJI,H.@USAGI : raw checksum (RFC2292(bis) compliance)
|
|
* Kazunori MIYAZAWA @USAGI: change process style to use ip6_append_data
|
|
+ * Hoerdt Mickael : Added Ipv6 multicast routing support.
|
|
+ *
|
|
+ * Changes:
|
|
+ * Kazunori MIYAZAWA @USAGI: change datagram transmit routine to ip6_append_data
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
@@ -55,6 +59,10 @@
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/seq_file.h>
|
|
|
|
+#ifdef CONFIG_IPV6_MROUTE
|
|
+#include <linux/mroute6.h>
|
|
+#endif
|
|
+
|
|
struct hlist_head raw_v6_htable[RAWV6_HTABLE_SIZE];
|
|
DEFINE_RWLOCK(raw_v6_lock);
|
|
|
|
@@ -162,7 +170,19 @@
|
|
sk = __raw_v6_lookup(sk, nexthdr, daddr, saddr);
|
|
|
|
while (sk) {
|
|
- if (nexthdr != IPPROTO_ICMPV6 || !icmpv6_filter(sk, skb)) {
|
|
+ int filtered;
|
|
+
|
|
+ switch (nexthdr) {
|
|
+ case IPPROTO_ICMPV6:
|
|
+ filtered = icmpv6_filter(sk, skb);
|
|
+ break;
|
|
+ default:
|
|
+ filtered = 0;
|
|
+ }
|
|
+
|
|
+ if (filtered < 0)
|
|
+ break;
|
|
+ if (filtered == 0) {
|
|
struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
|
|
|
|
/* Not releasing hash table! */
|
|
@@ -199,6 +219,10 @@
|
|
if (sk->sk_state != TCP_CLOSE)
|
|
goto out;
|
|
|
|
+ if (addr->sin6_port &&
|
|
+ ntohs(addr->sin6_port) != inet->num)
|
|
+ goto out;
|
|
+
|
|
/* Check if the address belongs to the host. */
|
|
if (addr_type != IPV6_ADDR_ANY) {
|
|
struct net_device *dev = NULL;
|
|
@@ -407,8 +431,11 @@
|
|
|
|
/* Copy the address. */
|
|
if (sin6) {
|
|
+ struct inet_sock *inet = inet_sk(sk);
|
|
+
|
|
sin6->sin6_family = AF_INET6;
|
|
ipv6_addr_copy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr);
|
|
+ sin6->sin6_port = htons(inet->num);
|
|
sin6->sin6_flowinfo = 0;
|
|
sin6->sin6_scope_id = 0;
|
|
if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)
|
|
@@ -509,6 +536,9 @@
|
|
struct inet_sock *inet = inet_sk(sk);
|
|
struct ipv6hdr *iph;
|
|
struct sk_buff *skb;
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct inet6_dev *idev = NULL;
|
|
+#endif
|
|
unsigned int hh_len;
|
|
int err;
|
|
|
|
@@ -539,7 +569,12 @@
|
|
if (err)
|
|
goto error_fault;
|
|
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ idev = rt->rt6i_idev;
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS);
|
|
+#endif
|
|
err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
|
|
dst_output);
|
|
if (err > 0)
|
|
@@ -553,7 +588,11 @@
|
|
err = -EFAULT;
|
|
kfree_skb(skb);
|
|
error:
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTDISCARDS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTDISCARDS);
|
|
+#endif
|
|
return err;
|
|
}
|
|
|
|
@@ -756,6 +795,8 @@
|
|
hlimit = np->hop_limit;
|
|
if (hlimit < 0)
|
|
hlimit = dst_metric(dst, RTAX_HOPLIMIT);
|
|
+ if (hlimit < 0)
|
|
+ hlimit = ipv6_get_hoplimit(dst->dev);
|
|
}
|
|
|
|
if (msg->msg_flags&MSG_CONFIRM)
|
|
@@ -952,7 +993,11 @@
|
|
}
|
|
|
|
default:
|
|
+#ifdef CONFIG_IPV6_MROUTE
|
|
+ return ip6mr_ioctl(sk,cmd,(void __user *)arg);
|
|
+#else
|
|
return -ENOIOCTLCMD;
|
|
+#endif
|
|
}
|
|
}
|
|
|
|
@@ -960,7 +1005,12 @@
|
|
{
|
|
if (inet_sk(sk)->num == IPPROTO_RAW)
|
|
ip6_ra_control(sk, -1, NULL);
|
|
-
|
|
+#ifdef CONFIG_IPV6_MROUTE
|
|
+ if (sk == mroute6_socket) {
|
|
+ printk(KERN_DEBUG "closing mroute6 socket.\n");
|
|
+ ip6_ra_control(sk, -1, NULL);
|
|
+ }
|
|
+#endif
|
|
sk_common_release(sk);
|
|
}
|
|
|
|
diff -urN linux-2.6.11/net/ipv6/reassembly.c x1/net/ipv6/reassembly.c
|
|
--- linux-2.6.11/net/ipv6/reassembly.c 2005-03-02 08:37:53.000000000 +0100
|
|
+++ x1/net/ipv6/reassembly.c 2005-02-13 19:37:11.000000000 +0100
|
|
@@ -53,6 +53,9 @@
|
|
#include <net/rawv6.h>
|
|
#include <net/ndisc.h>
|
|
#include <net/addrconf.h>
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+#include <net/ip6_fib.h>
|
|
+#endif
|
|
|
|
int sysctl_ip6frag_high_thresh = 256*1024;
|
|
int sysctl_ip6frag_low_thresh = 192*1024;
|
|
@@ -264,7 +267,7 @@
|
|
}
|
|
}
|
|
|
|
-static void ip6_evictor(void)
|
|
+static void ip6_evictor(struct inet6_dev *idev)
|
|
{
|
|
struct frag_queue *fq;
|
|
struct list_head *tmp;
|
|
@@ -291,14 +294,21 @@
|
|
spin_unlock(&fq->lock);
|
|
|
|
fq_put(fq, &work);
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_REASMFAILS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
|
|
+#endif
|
|
}
|
|
}
|
|
|
|
static void ip6_frag_expire(unsigned long data)
|
|
{
|
|
struct frag_queue *fq = (struct frag_queue *) data;
|
|
-
|
|
+ struct net_device *dev = NULL;
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct inet6_dev *idev = NULL;
|
|
+#endif
|
|
spin_lock(&fq->lock);
|
|
|
|
if (fq->last_in & COMPLETE)
|
|
@@ -306,13 +316,19 @@
|
|
|
|
fq_kill(fq);
|
|
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ dev = dev_get_by_index(fq->iif);
|
|
+ idev = dev ? in6_dev_get(dev) : NULL;
|
|
+
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_REASMTIMEOUT);
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_REASMFAILS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_REASMTIMEOUT);
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
|
|
+#endif
|
|
|
|
/* Send error only if the first segment arrived. */
|
|
if (fq->last_in&FIRST_IN && fq->fragments) {
|
|
- struct net_device *dev = dev_get_by_index(fq->iif);
|
|
-
|
|
/*
|
|
But use as source device on which LAST ARRIVED
|
|
segment was received. And do not use fq->dev
|
|
@@ -322,9 +338,14 @@
|
|
fq->fragments->dev = dev;
|
|
icmpv6_send(fq->fragments, ICMPV6_TIME_EXCEED, ICMPV6_EXC_FRAGTIME, 0,
|
|
dev);
|
|
- dev_put(dev);
|
|
}
|
|
}
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ if (idev)
|
|
+ in6_dev_put(idev);
|
|
+ if (dev)
|
|
+ dev_put(dev);
|
|
+#endif
|
|
out:
|
|
spin_unlock(&fq->lock);
|
|
fq_put(fq, NULL);
|
|
@@ -371,7 +392,11 @@
|
|
|
|
|
|
static struct frag_queue *
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ip6_frag_create(unsigned int hash, u32 id, struct in6_addr *src, struct in6_addr *dst, struct inet6_dev *idev)
|
|
+#else
|
|
ip6_frag_create(unsigned int hash, u32 id, struct in6_addr *src, struct in6_addr *dst)
|
|
+#endif
|
|
{
|
|
struct frag_queue *fq;
|
|
|
|
@@ -393,12 +418,20 @@
|
|
return ip6_frag_intern(hash, fq);
|
|
|
|
oom:
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_REASMFAILS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
|
|
+#endif
|
|
return NULL;
|
|
}
|
|
|
|
static __inline__ struct frag_queue *
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+fq_find(u32 id, struct in6_addr *src, struct in6_addr *dst, struct inet6_dev *idev)
|
|
+#else
|
|
fq_find(u32 id, struct in6_addr *src, struct in6_addr *dst)
|
|
+#endif
|
|
{
|
|
struct frag_queue *fq;
|
|
unsigned int hash = ip6qhashfn(id, src, dst);
|
|
@@ -415,7 +448,11 @@
|
|
}
|
|
read_unlock(&ip6_frag_lock);
|
|
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ return ip6_frag_create(hash, id, src, dst, idev);
|
|
+#else
|
|
return ip6_frag_create(hash, id, src, dst);
|
|
+#endif
|
|
}
|
|
|
|
|
|
@@ -423,6 +460,11 @@
|
|
struct frag_hdr *fhdr, int nhoff)
|
|
{
|
|
struct sk_buff *prev, *next;
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct dst_entry *dst = skb->dst;
|
|
+ struct inet6_dev *idev = ((struct rt6_info *)dst)->rt6i_idev;
|
|
+#endif
|
|
+
|
|
int offset, end;
|
|
|
|
if (fq->last_in & COMPLETE)
|
|
@@ -433,7 +475,11 @@
|
|
((u8 *) (fhdr + 1) - (u8 *) (skb->nh.ipv6h + 1)));
|
|
|
|
if ((unsigned int)end > IPV6_MAXPLEN) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
|
|
+#endif
|
|
icmpv6_param_prob(skb,ICMPV6_HDR_FIELD, (u8*)&fhdr->frag_off - skb->nh.raw);
|
|
return;
|
|
}
|
|
@@ -460,7 +506,11 @@
|
|
/* RFC2460 says always send parameter problem in
|
|
* this case. -DaveM
|
|
*/
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_INHDRERRORS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
|
|
+#endif
|
|
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD,
|
|
offsetof(struct ipv6hdr, payload_len));
|
|
return;
|
|
@@ -579,7 +629,11 @@
|
|
return;
|
|
|
|
err:
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_REASMFAILS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_REASMFAILS);
|
|
+#endif
|
|
kfree_skb(skb);
|
|
}
|
|
|
|
@@ -597,6 +651,11 @@
|
|
struct net_device *dev)
|
|
{
|
|
struct sk_buff *fp, *head = fq->fragments;
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct sk_buff *skb = *skb_in;
|
|
+ struct dst_entry *dst = skb->dst;
|
|
+ struct inet6_dev *idev = ((struct rt6_info *)dst)->rt6i_idev;
|
|
+#endif
|
|
int payload_len;
|
|
unsigned int nhoff;
|
|
|
|
@@ -673,7 +732,11 @@
|
|
if (head->ip_summed == CHECKSUM_HW)
|
|
head->csum = csum_partial(head->nh.raw, head->h.raw-head->nh.raw, head->csum);
|
|
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_REASMOKS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_REASMOKS);
|
|
+#endif
|
|
fq->fragments = NULL;
|
|
*nhoffp = nhoff;
|
|
return 1;
|
|
@@ -686,7 +749,11 @@
|
|
if (net_ratelimit())
|
|
printk(KERN_DEBUG "ip6_frag_reasm: no memory for reassembly\n");
|
|
out_fail:
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_REASMFAILS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
|
|
+#endif
|
|
return -1;
|
|
}
|
|
|
|
@@ -697,19 +764,35 @@
|
|
struct frag_hdr *fhdr;
|
|
struct frag_queue *fq;
|
|
struct ipv6hdr *hdr;
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct dst_entry *dst = skb->dst;
|
|
+ struct inet6_dev *idev = ((struct rt6_info *)dst)->rt6i_idev;
|
|
+#endif
|
|
|
|
hdr = skb->nh.ipv6h;
|
|
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_REASMREQDS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_REASMREQDS);
|
|
+#endif
|
|
|
|
/* Jumbo payload inhibits frag. header */
|
|
if (hdr->payload_len==0) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_INHDRERRORS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_INHDRERRORS);
|
|
+#endif
|
|
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw-skb->nh.raw);
|
|
return -1;
|
|
}
|
|
if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+sizeof(struct frag_hdr))) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_INHDRERRORS);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_INHDRERRORS);
|
|
+#endif
|
|
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, skb->h.raw-skb->nh.raw);
|
|
return -1;
|
|
}
|
|
@@ -720,16 +803,29 @@
|
|
if (!(fhdr->frag_off & htons(0xFFF9))) {
|
|
/* It is not a fragmented frame */
|
|
skb->h.raw += sizeof(struct frag_hdr);
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_REASMOKS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_REASMOKS);
|
|
+#endif
|
|
|
|
*nhoffp = (u8*)fhdr - skb->nh.raw;
|
|
return 1;
|
|
}
|
|
|
|
- if (atomic_read(&ip6_frag_mem) > sysctl_ip6frag_high_thresh)
|
|
- ip6_evictor();
|
|
+ if (atomic_read(&ip6_frag_mem) > sysctl_ip6frag_high_thresh) {
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ ip6_evictor(idev);
|
|
+#else
|
|
+ ip6_evictor(NULL);
|
|
+#endif
|
|
+ }
|
|
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ if ((fq = fq_find(fhdr->identification, &hdr->saddr, &hdr->daddr, idev)) != NULL) {
|
|
+#else
|
|
if ((fq = fq_find(fhdr->identification, &hdr->saddr, &hdr->daddr)) != NULL) {
|
|
+#endif
|
|
int ret = -1;
|
|
|
|
spin_lock(&fq->lock);
|
|
@@ -745,7 +841,11 @@
|
|
return ret;
|
|
}
|
|
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ IP6_INC_STATS_BH(idev, IPSTATS_MIB_REASMFAILS);
|
|
+#else
|
|
IP6_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
|
|
+#endif
|
|
kfree_skb(skb);
|
|
return -1;
|
|
}
|
|
diff -urN linux-2.6.11/net/ipv6/route.c x1/net/ipv6/route.c
|
|
--- linux-2.6.11/net/ipv6/route.c 2005-03-02 08:38:17.000000000 +0100
|
|
+++ x1/net/ipv6/route.c 2005-02-28 07:45:55.000000000 +0100
|
|
@@ -136,7 +136,6 @@
|
|
|
|
DEFINE_RWLOCK(rt6_lock);
|
|
|
|
-
|
|
/* allocate dst with ip6_dst_ops */
|
|
static __inline__ struct rt6_info *ip6_dst_alloc(void)
|
|
{
|
|
@@ -216,8 +215,9 @@
|
|
/*
|
|
* pointer to the last default router chosen. BH is disabled locally.
|
|
*/
|
|
-static struct rt6_info *rt6_dflt_pointer;
|
|
-static DEFINE_SPINLOCK(rt6_dflt_lock);
|
|
+#if !defined(CONFIG_IPV6_NEW_ROUNDROBIN)
|
|
+struct rt6_info *rt6_dflt_pointer;
|
|
+spinlock_t rt6_dflt_lock = SPIN_LOCK_UNLOCKED;
|
|
|
|
void rt6_reset_dflt_pointer(struct rt6_info *rt)
|
|
{
|
|
@@ -228,61 +228,168 @@
|
|
}
|
|
spin_unlock_bh(&rt6_dflt_lock);
|
|
}
|
|
+#endif
|
|
|
|
/* Default Router Selection (RFC 2461 6.3.6) */
|
|
-static struct rt6_info *rt6_best_dflt(struct rt6_info *rt, int oif)
|
|
+static int __rt6_score_dflt(struct rt6_info *sprt, struct rt6_info *dflt, int oif)
|
|
{
|
|
- struct rt6_info *match = NULL;
|
|
- struct rt6_info *sprt;
|
|
- int mpri = 0;
|
|
+ struct neighbour *neigh = sprt->rt6i_nexthop;
|
|
+ int m = oif ? 0 : 8;
|
|
|
|
- for (sprt = rt; sprt; sprt = sprt->u.next) {
|
|
- struct neighbour *neigh;
|
|
- int m = 0;
|
|
+ if (!neigh)
|
|
+ return -1;
|
|
|
|
- if (!oif ||
|
|
- (sprt->rt6i_dev &&
|
|
- sprt->rt6i_dev->ifindex == oif))
|
|
- m += 8;
|
|
+ if (rt6_check_expired(sprt))
|
|
+ return -1;
|
|
|
|
- if (rt6_check_expired(sprt))
|
|
- continue;
|
|
+ if (oif && sprt->rt6i_dev &&
|
|
+ sprt->rt6i_dev->ifindex == oif)
|
|
+ m += 8;
|
|
+
|
|
+#if !defined(CONFIG_IPV6_ROUTER_PREF)
|
|
+ if (sprt == dflt)
|
|
+ m += 4;
|
|
+#endif
|
|
|
|
- if (sprt == rt6_dflt_pointer)
|
|
- m += 4;
|
|
+ read_lock_bh(&neigh->lock);
|
|
+ switch (neigh->nud_state) {
|
|
+ case NUD_REACHABLE:
|
|
+ m += 3;
|
|
+ break;
|
|
|
|
- if ((neigh = sprt->rt6i_nexthop) != NULL) {
|
|
- read_lock_bh(&neigh->lock);
|
|
- switch (neigh->nud_state) {
|
|
- case NUD_REACHABLE:
|
|
- m += 3;
|
|
- break;
|
|
+ case NUD_STALE:
|
|
+ case NUD_DELAY:
|
|
+ case NUD_PROBE:
|
|
+ m += 2;
|
|
+ break;
|
|
|
|
- case NUD_STALE:
|
|
- case NUD_DELAY:
|
|
- case NUD_PROBE:
|
|
- m += 2;
|
|
- break;
|
|
+ case NUD_NOARP:
|
|
+ case NUD_PERMANENT:
|
|
+ m += 1;
|
|
+ break;
|
|
|
|
- case NUD_NOARP:
|
|
- case NUD_PERMANENT:
|
|
- m += 1;
|
|
+ case NUD_INCOMPLETE:
|
|
+ default:
|
|
+ m = -1;
|
|
+ }
|
|
+ read_unlock_bh(&neigh->lock);
|
|
+
|
|
+ return m;
|
|
+}
|
|
+
|
|
+static struct rt6_info *rt6_best_dflt(struct rt6_info *rt, struct rt6_info **head, int oif)
|
|
+{
|
|
+ struct rt6_info *match = NULL;
|
|
+ struct rt6_info *sprt;
|
|
+#if defined(CONFIG_IPV6_NEW_ROUNDROBIN)
|
|
+ struct rt6_info *last = NULL;
|
|
+#endif
|
|
+ int mpri = 0;
|
|
+#if defined(CONFIG_IPV6_ROUTER_PREF)
|
|
+ u32 metric = 0;
|
|
+ int pref = -3;
|
|
+#else
|
|
+ static const int okpri = 12; /* device match, prob. reachable */
|
|
+#endif
|
|
+
|
|
+ if (head != NULL && *head != rt)
|
|
+ head = NULL; /*XXX*/
|
|
+
|
|
+#if !defined(CONFIG_IPV6_NEW_ROUNDROBIN)
|
|
+ spin_lock(&rt6_dflt_lock);
|
|
+#endif
|
|
+#if !defined(CONFIG_IPV6_NEW_ROUNDROBIN)
|
|
+ if (rt6_dflt_pointer) {
|
|
+ for (sprt = rt; sprt; sprt = sprt->u.next) {
|
|
+ if (sprt == rt6_dflt_pointer)
|
|
break;
|
|
+ }
|
|
+ if (!sprt)
|
|
+ rt6_dflt_pointer = NULL; /* for sure */
|
|
+ }
|
|
+#endif
|
|
|
|
- case NUD_INCOMPLETE:
|
|
- default:
|
|
- read_unlock_bh(&neigh->lock);
|
|
+#if defined(CONFIG_IPV6_ROUTER_PREF)
|
|
+#if !defined(CONFIG_IPV6_NEW_ROUNDROBIN)
|
|
+ if (rt6_dflt_pointer) {
|
|
+ for (sprt = rt6_dflt_pointer->u.next; sprt; sprt = sprt->u.next) {
|
|
+ int m, p;
|
|
+
|
|
+ if ((metric != 0 && sprt->rt6i_metric > metric) ||
|
|
+ sprt->u.dst.obsolete > 0 ||
|
|
+ sprt->u.dst.error != 0)
|
|
continue;
|
|
+
|
|
+#if defined(CONFIG_IPV6_NEW_ROUNDROBIN)
|
|
+ m = __rt6_score_dflt(sprt, rt, oif);
|
|
+#else
|
|
+ m = __rt6_score_dflt(sprt, rt6_dflt_pointer, oif);
|
|
+#endif
|
|
+ if (m < mpri)
|
|
+ continue;
|
|
+ p = IPV6_SIGNEDPREF(IPV6_UNSHIFT_PREF(sprt->rt6i_flags));
|
|
+ if (sprt->rt6i_metric < metric || m > mpri || p > pref) {
|
|
+ match = sprt;
|
|
+ metric = sprt->rt6i_metric;
|
|
+ mpri = m;
|
|
+ pref = p;
|
|
}
|
|
- read_unlock_bh(&neigh->lock);
|
|
- } else {
|
|
+ }
|
|
+ }
|
|
+#endif
|
|
+#if defined(CONFIG_IPV6_NEW_ROUNDROBIN)
|
|
+ metric = rt->rt6i_metric;
|
|
+#endif
|
|
+ for (sprt = rt; sprt; sprt = sprt->u.next) {
|
|
+ int m, p;
|
|
+#if defined(CONFIG_IPV6_NEW_ROUNDROBIN)
|
|
+ if (sprt->rt6i_metric > metric)
|
|
+ break;
|
|
+#else
|
|
+ if ((metric != 0 && sprt->rt6i_metric > metric) ||
|
|
+ sprt->u.dst.obsolete > 0 ||
|
|
+ sprt->u.dst.error != 0)
|
|
continue;
|
|
+
|
|
+#endif
|
|
+#if defined(CONFIG_IPV6_NEW_ROUNDROBIN)
|
|
+ m = __rt6_score_dflt(sprt, rt, oif);
|
|
+#else
|
|
+ m = __rt6_score_dflt(sprt, rt6_dflt_pointer, oif);
|
|
+#endif
|
|
+ if (m < mpri)
|
|
+ continue;
|
|
+ p = IPV6_SIGNEDPREF(IPV6_UNSHIFT_PREF(sprt->rt6i_flags));
|
|
+ if (
|
|
+#if !defined(CONFIG_IPV6_NEW_ROUNDROBIN)
|
|
+ sprt->rt6i_metric < metric ||
|
|
+#endif
|
|
+ m > mpri || p > pref) {
|
|
+ match = sprt;
|
|
+ metric = sprt->rt6i_metric;
|
|
+ mpri = m;
|
|
+ pref = p;
|
|
}
|
|
+#if defined(CONFIG_IPV6_NEW_ROUNDROBIN)
|
|
+ last = sprt;
|
|
+#else
|
|
+ if (sprt == rt6_dflt_pointer)
|
|
+ break;
|
|
+#endif
|
|
+ }
|
|
+#else /* CONFIG_IPV6_ROUTER_PREF / !CONFIG_IPV6_ROUTER_PREF */
|
|
+ for (sprt = rt; sprt; sprt = sprt->u.next) {
|
|
+ int m;
|
|
+#if defined(CONFIG_IPV6_NEW_ROUNDROBIN)
|
|
+ m = __rt6_score_dflt(sprt, rt, oif);
|
|
+#else
|
|
+ m = __rt6_score_dflt(sprt, rt6_dflt_pointer, oif);
|
|
+#endif
|
|
|
|
- if (m > mpri || m >= 12) {
|
|
+ if (m > mpri || m >= okpri) {
|
|
match = sprt;
|
|
mpri = m;
|
|
- if (m >= 12) {
|
|
+ if (m >= okpri) {
|
|
/* we choose the last default router if it
|
|
* is in (probably) reachable state.
|
|
* If route changed, we should do pmtu
|
|
@@ -293,7 +400,6 @@
|
|
}
|
|
}
|
|
|
|
- spin_lock(&rt6_dflt_lock);
|
|
if (!match) {
|
|
/*
|
|
* No default routers are known to be reachable.
|
|
@@ -323,14 +429,25 @@
|
|
}
|
|
}
|
|
}
|
|
+#endif /* !CONFIG_IPV6_ROUTER_PREF */
|
|
|
|
if (match) {
|
|
+#if defined(CONFIG_IPV6_NEW_ROUNDROBIN)
|
|
+ if (rt != last && last) {
|
|
+ *head = rt->u.next;
|
|
+ rt->u.next = last->u.next;
|
|
+ last->u.next = rt;
|
|
+ }
|
|
+#else
|
|
if (rt6_dflt_pointer != match)
|
|
RT6_TRACE("changed default router: %p->%p\n",
|
|
rt6_dflt_pointer, match);
|
|
rt6_dflt_pointer = match;
|
|
+#endif
|
|
}
|
|
+#if !defined(CONFIG_IPV6_NEW_ROUNDROBIN)
|
|
spin_unlock(&rt6_dflt_lock);
|
|
+#endif
|
|
|
|
if (!match) {
|
|
/*
|
|
@@ -540,7 +657,7 @@
|
|
}
|
|
if (rt->rt6i_flags & RTF_DEFAULT) {
|
|
if (rt->rt6i_metric >= IP6_RT_PRIO_ADDRCONF)
|
|
- rt = rt6_best_dflt(rt, fl->oif);
|
|
+ rt = rt6_best_dflt(rt, &fn->leaf, fl->oif);
|
|
} else {
|
|
rt = rt6_device_match(rt, fl->oif, strict);
|
|
BACKTRACK();
|
|
@@ -575,7 +692,6 @@
|
|
return &rt->u.dst;
|
|
}
|
|
|
|
-
|
|
/*
|
|
* Destination cache support functions
|
|
*/
|
|
@@ -628,8 +744,10 @@
|
|
|
|
if (mtu < dst_pmtu(dst) && rt6->rt6i_dst.plen == 128) {
|
|
rt6->rt6i_flags |= RTF_MODIFIED;
|
|
- if (mtu < IPV6_MIN_MTU)
|
|
+ if (mtu < IPV6_MIN_MTU) {
|
|
mtu = IPV6_MIN_MTU;
|
|
+ dst->metrics[RTAX_FEATURES-1] |= RTAX_FEATURE_ALLFRAG;
|
|
+ }
|
|
dst->metrics[RTAX_MTU-1] = mtu;
|
|
}
|
|
}
|
|
@@ -769,7 +887,7 @@
|
|
return mtu;
|
|
}
|
|
|
|
-static int ipv6_get_hoplimit(struct net_device *dev)
|
|
+int ipv6_get_hoplimit(struct net_device *dev)
|
|
{
|
|
int hoplimit = ipv6_devconf.hop_limit;
|
|
struct inet6_dev *idev;
|
|
@@ -965,14 +1083,8 @@
|
|
}
|
|
}
|
|
|
|
- if (rt->u.dst.metrics[RTAX_HOPLIMIT-1] == 0) {
|
|
- if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr))
|
|
- rt->u.dst.metrics[RTAX_HOPLIMIT-1] =
|
|
- IPV6_DEFAULT_MCASTHOPS;
|
|
- else
|
|
- rt->u.dst.metrics[RTAX_HOPLIMIT-1] =
|
|
- ipv6_get_hoplimit(dev);
|
|
- }
|
|
+ if (rt->u.dst.metrics[RTAX_HOPLIMIT-1] == 0)
|
|
+ rt->u.dst.metrics[RTAX_HOPLIMIT-1] = -1;
|
|
|
|
if (!rt->u.dst.metrics[RTAX_MTU-1])
|
|
rt->u.dst.metrics[RTAX_MTU-1] = ipv6_get_mtu(dev);
|
|
@@ -998,7 +1110,9 @@
|
|
|
|
write_lock_bh(&rt6_lock);
|
|
|
|
+#ifndef CONFIG_IPV6_NEW_ROUNDROBIN
|
|
rt6_reset_dflt_pointer(NULL);
|
|
+#endif
|
|
|
|
err = fib6_del(rt, nlh, _rtattr);
|
|
dst_release(&rt->u.dst);
|
|
@@ -1051,11 +1165,24 @@
|
|
{
|
|
struct rt6_info *rt, *nrt;
|
|
|
|
+#ifdef CONFIG_IPV6_NDISC_DEBUG
|
|
+ printk(KERN_DEBUG
|
|
+ "%s("
|
|
+ "dest=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x, "
|
|
+ "saddr=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x, "
|
|
+ "neigh=%p(%s), "
|
|
+ "lladdr=%p, on_link=%d)\n",
|
|
+ __FUNCTION__,
|
|
+ NIP6(*dest), NIP6(*saddr),
|
|
+ neigh, neigh_state(neigh->nud_state),
|
|
+ lladdr, on_link);
|
|
+#endif
|
|
+
|
|
/* Locate old route to this destination. */
|
|
rt = rt6_lookup(dest, NULL, neigh->dev->ifindex, 1);
|
|
|
|
if (rt == NULL)
|
|
- return;
|
|
+ goto out;
|
|
|
|
if (neigh->dev != rt->rt6i_dev)
|
|
goto out;
|
|
@@ -1068,8 +1195,12 @@
|
|
* But then router serving it might decide, that we should
|
|
* know truth 8)8) --ANK (980726).
|
|
*/
|
|
- if (!(rt->rt6i_flags&RTF_GATEWAY))
|
|
+ if (!(rt->rt6i_flags&RTF_GATEWAY)) {
|
|
+ if (net_ratelimit())
|
|
+ printk(KERN_DEBUG "%s(): rt=%p is on-link; ignored.\n",
|
|
+ __FUNCTION__, rt);
|
|
goto out;
|
|
+ }
|
|
|
|
/*
|
|
* RFC 2461 specifies that redirects should only be
|
|
@@ -1101,9 +1232,8 @@
|
|
}
|
|
|
|
source_ok:
|
|
-
|
|
/*
|
|
- * We have finally decided to accept it.
|
|
+ * Okay, we have finally decided to accept it.
|
|
*/
|
|
|
|
neigh_update(neigh, lladdr, NUD_STALE,
|
|
@@ -1152,7 +1282,6 @@
|
|
|
|
out:
|
|
dst_release(&rt->u.dst);
|
|
- return;
|
|
}
|
|
|
|
/*
|
|
@@ -1164,17 +1293,7 @@
|
|
struct net_device *dev, u32 pmtu)
|
|
{
|
|
struct rt6_info *rt, *nrt;
|
|
-
|
|
- if (pmtu < IPV6_MIN_MTU) {
|
|
- if (net_ratelimit())
|
|
- printk(KERN_DEBUG "rt6_pmtu_discovery: invalid MTU value %d\n",
|
|
- pmtu);
|
|
- /* According to RFC1981, the PMTU is set to the IPv6 minimum
|
|
- link MTU if the node receives a Packet Too Big message
|
|
- reporting next-hop MTU that is less than the IPv6 minimum MTU.
|
|
- */
|
|
- pmtu = IPV6_MIN_MTU;
|
|
- }
|
|
+ int allfrag = 0;
|
|
|
|
rt = rt6_lookup(daddr, saddr, dev->ifindex, 0);
|
|
|
|
@@ -1184,6 +1303,17 @@
|
|
if (pmtu >= dst_pmtu(&rt->u.dst))
|
|
goto out;
|
|
|
|
+ if (pmtu < IPV6_MIN_MTU) {
|
|
+ /*
|
|
+ * According to RFC2461, pmtu is set to the IPv6 minimum MTU
|
|
+ * (1280) and a fragment header is included after a node
|
|
+ * receiving Too Big message reporting PMTU is less than
|
|
+ * the IPv6 minimum MTU.
|
|
+ */
|
|
+ pmtu = IPV6_MIN_MTU;
|
|
+ allfrag = 1;
|
|
+ }
|
|
+
|
|
/* New mtu received -> path was valid.
|
|
They are sent only in response to data packets,
|
|
so that this nexthop apparently is reachable. --ANK
|
|
@@ -1197,6 +1327,8 @@
|
|
*/
|
|
if (rt->rt6i_flags & RTF_CACHE) {
|
|
rt->u.dst.metrics[RTAX_MTU-1] = pmtu;
|
|
+ if (allfrag)
|
|
+ rt->u.dst.metrics[RTAX_FEATURES-1] |= RTAX_FEATURE_ALLFRAG;
|
|
dst_set_expires(&rt->u.dst, ip6_rt_mtu_expires);
|
|
rt->rt6i_flags |= RTF_MODIFIED|RTF_EXPIRES;
|
|
goto out;
|
|
@@ -1211,6 +1343,8 @@
|
|
nrt = rt6_cow(rt, daddr, saddr);
|
|
if (!nrt->u.dst.error) {
|
|
nrt->u.dst.metrics[RTAX_MTU-1] = pmtu;
|
|
+ if (allfrag)
|
|
+ nrt->u.dst.metrics[RTAX_FEATURES-1] |= RTAX_FEATURE_ALLFRAG;
|
|
/* According to RFC 1981, detecting PMTU increase shouldn't be
|
|
happened within 5 mins, the recommended timer is 10 mins.
|
|
Here this route expiration time is set to ip6_rt_mtu_expires
|
|
@@ -1232,6 +1366,8 @@
|
|
dst_set_expires(&nrt->u.dst, ip6_rt_mtu_expires);
|
|
nrt->rt6i_flags |= RTF_DYNAMIC|RTF_CACHE|RTF_EXPIRES;
|
|
nrt->u.dst.metrics[RTAX_MTU-1] = pmtu;
|
|
+ if (allfrag)
|
|
+ nrt->u.dst.metrics[RTAX_FEATURES-1] |= RTAX_FEATURE_ALLFRAG;
|
|
ip6_ins_rt(nrt, NULL, NULL);
|
|
}
|
|
|
|
@@ -1293,7 +1429,8 @@
|
|
}
|
|
|
|
struct rt6_info *rt6_add_dflt_router(struct in6_addr *gwaddr,
|
|
- struct net_device *dev)
|
|
+ struct net_device *dev,
|
|
+ int pref)
|
|
{
|
|
struct in6_rtmsg rtmsg;
|
|
|
|
@@ -1301,7 +1438,7 @@
|
|
rtmsg.rtmsg_type = RTMSG_NEWROUTE;
|
|
ipv6_addr_copy(&rtmsg.rtmsg_gateway, gwaddr);
|
|
rtmsg.rtmsg_metric = 1024;
|
|
- rtmsg.rtmsg_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | RTF_UP | RTF_EXPIRES;
|
|
+ rtmsg.rtmsg_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | RTF_UP | RTF_PREF(pref&3) | RTF_EXPIRES;
|
|
|
|
rtmsg.rtmsg_ifindex = dev->ifindex;
|
|
|
|
@@ -1319,7 +1456,9 @@
|
|
if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) {
|
|
dst_hold(&rt->u.dst);
|
|
|
|
+#ifndef CONFIG_IPV6_NEW_ROUNDROBIN
|
|
rt6_reset_dflt_pointer(NULL);
|
|
+#endif
|
|
|
|
read_unlock_bh(&rt6_lock);
|
|
|
|
@@ -1371,7 +1510,13 @@
|
|
|
|
int ip6_pkt_discard(struct sk_buff *skb)
|
|
{
|
|
+#ifdef CONFIG_IPV6_STATISTICS
|
|
+ struct dst_entry *dst = skb->dst;
|
|
+ struct inet6_dev *idev = ((struct rt6_info *)dst)->rt6i_idev;
|
|
+ IP6_INC_STATS(idev, IPSTATS_MIB_OUTNOROUTES);
|
|
+#else
|
|
IP6_INC_STATS(IPSTATS_MIB_OUTNOROUTES);
|
|
+#endif
|
|
icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_NOROUTE, 0, skb->dev);
|
|
kfree_skb(skb);
|
|
return 0;
|
|
@@ -1406,7 +1551,7 @@
|
|
rt->rt6i_idev = idev;
|
|
rt->u.dst.metrics[RTAX_MTU-1] = ipv6_get_mtu(rt->rt6i_dev);
|
|
rt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(dst_pmtu(&rt->u.dst));
|
|
- rt->u.dst.metrics[RTAX_HOPLIMIT-1] = ipv6_get_hoplimit(rt->rt6i_dev);
|
|
+ rt->u.dst.metrics[RTAX_HOPLIMIT-1] = -1;
|
|
rt->u.dst.obsolete = -1;
|
|
|
|
rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
|
|
@@ -1877,7 +2022,6 @@
|
|
static int rt6_info_route(struct rt6_info *rt, void *p_arg)
|
|
{
|
|
struct rt6_proc_arg *arg = (struct rt6_proc_arg *) p_arg;
|
|
- int i;
|
|
|
|
if (arg->skip < arg->offset / RT6_INFO_LEN) {
|
|
arg->skip++;
|
|
@@ -1887,39 +2031,29 @@
|
|
if (arg->len >= arg->length)
|
|
return 0;
|
|
|
|
- for (i=0; i<16; i++) {
|
|
- sprintf(arg->buffer + arg->len, "%02x",
|
|
- rt->rt6i_dst.addr.s6_addr[i]);
|
|
- arg->len += 2;
|
|
- }
|
|
- arg->len += sprintf(arg->buffer + arg->len, " %02x ",
|
|
+ arg->len += sprintf(arg->buffer + arg->len,
|
|
+ "%04x%04x%04x%04x%04x%04x%04x%04x %02x ",
|
|
+ NIP6(rt->rt6i_dst.addr),
|
|
rt->rt6i_dst.plen);
|
|
|
|
#ifdef CONFIG_IPV6_SUBTREES
|
|
- for (i=0; i<16; i++) {
|
|
- sprintf(arg->buffer + arg->len, "%02x",
|
|
- rt->rt6i_src.addr.s6_addr[i]);
|
|
- arg->len += 2;
|
|
- }
|
|
- arg->len += sprintf(arg->buffer + arg->len, " %02x ",
|
|
+ arg->len += sprintf(arg->buffer + arg->len,
|
|
+ "%04x%04x%04x%04x%04x%04x%04x%04x %02x ",
|
|
+ NIP6(rt->rt6i_src.addr),
|
|
rt->rt6i_src.plen);
|
|
#else
|
|
- sprintf(arg->buffer + arg->len,
|
|
- "00000000000000000000000000000000 00 ");
|
|
- arg->len += 36;
|
|
+ arg->len += sprintf(arg->buffer + arg->len,
|
|
+ "00000000000000000000000000000000 00 ");
|
|
#endif
|
|
|
|
- if (rt->rt6i_nexthop) {
|
|
- for (i=0; i<16; i++) {
|
|
- sprintf(arg->buffer + arg->len, "%02x",
|
|
- rt->rt6i_nexthop->primary_key[i]);
|
|
- arg->len += 2;
|
|
- }
|
|
- } else {
|
|
- sprintf(arg->buffer + arg->len,
|
|
- "00000000000000000000000000000000");
|
|
- arg->len += 32;
|
|
- }
|
|
+ if (rt->rt6i_nexthop)
|
|
+ arg->len += sprintf(arg->buffer + arg->len,
|
|
+ "%04x%04x%04x%04x%04x%04x%04x%04x",
|
|
+ NIP6(*((struct in6_addr *)rt->rt6i_nexthop->primary_key)));
|
|
+ else
|
|
+ arg->len += sprintf(arg->buffer + arg->len,
|
|
+ "00000000000000000000000000000000");
|
|
+
|
|
arg->len += sprintf(arg->buffer + arg->len,
|
|
" %08x %08x %08x %08x %8s\n",
|
|
rt->rt6i_metric, atomic_read(&rt->u.dst.__refcnt),
|
|
@@ -2080,6 +2214,15 @@
|
|
.proc_handler = &proc_dointvec_jiffies,
|
|
.strategy = &sysctl_jiffies,
|
|
},
|
|
+ {
|
|
+ .ctl_name = NET_IPV6_ROUTE_GC_MIN_INTERVAL_MS,
|
|
+ .procname = "gc_min_interval_ms",
|
|
+ .data = &ip6_rt_gc_min_interval,
|
|
+ .maxlen = sizeof(int),
|
|
+ .mode = 0644,
|
|
+ .proc_handler = &proc_dointvec_ms_jiffies,
|
|
+ .strategy = &sysctl_ms_jiffies,
|
|
+ },
|
|
{ .ctl_name = 0 }
|
|
};
|
|
|
|
diff -urN linux-2.6.11/net/ipv6/udp.c x1/net/ipv6/udp.c
|
|
--- linux-2.6.11/net/ipv6/udp.c 2005-03-02 08:38:20.000000000 +0100
|
|
+++ x1/net/ipv6/udp.c 2005-02-28 07:45:55.000000000 +0100
|
|
@@ -277,7 +277,6 @@
|
|
if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL)
|
|
sin6->sin6_scope_id = IP6CB(skb)->iif;
|
|
}
|
|
-
|
|
}
|
|
if (skb->protocol == htons(ETH_P_IP)) {
|
|
if (inet->cmsg_flags)
|
|
@@ -811,6 +810,8 @@
|
|
hlimit = np->hop_limit;
|
|
if (hlimit < 0)
|
|
hlimit = dst_metric(dst, RTAX_HOPLIMIT);
|
|
+ if (hlimit < 0)
|
|
+ hlimit = ipv6_get_hoplimit(dst->dev);
|
|
}
|
|
|
|
if (msg->msg_flags&MSG_CONFIRM)
|
|
diff -urN linux-2.6.11/net/ipv6/xfrm6_input.c x1/net/ipv6/xfrm6_input.c
|
|
--- linux-2.6.11/net/ipv6/xfrm6_input.c 2005-03-02 08:38:33.000000000 +0100
|
|
+++ x1/net/ipv6/xfrm6_input.c 2004-09-30 15:26:35.000000000 +0200
|
|
@@ -42,7 +42,7 @@
|
|
nexthdr = skb->nh.raw[nhoff];
|
|
|
|
seq = 0;
|
|
- if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) != 0)
|
|
+ if (!spi && (err = xfrm_parse_spi(skb, nexthdr, &spi)) != 0)
|
|
goto drop;
|
|
|
|
do {
|
|
@@ -58,9 +58,6 @@
|
|
if (unlikely(x->km.state != XFRM_STATE_VALID))
|
|
goto drop_unlock;
|
|
|
|
- if (x->props.replay_window && xfrm_replay_check(x, seq))
|
|
- goto drop_unlock;
|
|
-
|
|
if (xfrm_state_check_expire(x))
|
|
goto drop_unlock;
|
|
|
|
@@ -70,9 +67,6 @@
|
|
|
|
skb->nh.raw[nhoff] = nexthdr;
|
|
|
|
- if (x->props.replay_window)
|
|
- xfrm_replay_advance(x, seq);
|
|
-
|
|
x->curlft.bytes += skb->len;
|
|
x->curlft.packets++;
|
|
|
|
@@ -99,8 +93,9 @@
|
|
break;
|
|
}
|
|
|
|
- if ((err = xfrm_parse_spi(skb, nexthdr, &spi, &seq)) < 0)
|
|
+ if ((err = xfrm_parse_spi(skb, nexthdr, &spi)) < 0)
|
|
goto drop;
|
|
+
|
|
} while (!err);
|
|
|
|
/* Allocate new secpath or COW existing one. */
|
|
diff -urN linux-2.6.11/net/ipv6/xfrm6_policy.c x1/net/ipv6/xfrm6_policy.c
|
|
--- linux-2.6.11/net/ipv6/xfrm6_policy.c 2005-03-02 08:37:50.000000000 +0100
|
|
+++ x1/net/ipv6/xfrm6_policy.c 2004-11-25 06:33:10.000000000 +0100
|
|
@@ -25,8 +25,8 @@
|
|
static int xfrm6_dst_lookup(struct xfrm_dst **dst, struct flowi *fl)
|
|
{
|
|
int err = 0;
|
|
- *dst = (struct xfrm_dst*)ip6_route_output(NULL, fl);
|
|
- if (!*dst)
|
|
+ err = ip6_dst_lookup(NULL, (struct dst_entry **)dst, fl);
|
|
+ if (err)
|
|
err = -ENETUNREACH;
|
|
return err;
|
|
}
|
|
@@ -56,7 +56,6 @@
|
|
{
|
|
struct dst_entry *dst;
|
|
|
|
- /* Still not clear if we should set fl->fl6_{src,dst}... */
|
|
read_lock_bh(&policy->lock);
|
|
for (dst = policy->bundles; dst; dst = dst->next) {
|
|
struct xfrm_dst *xdst = (struct xfrm_dst*)dst;
|
|
diff -urN linux-2.6.11/net/xfrm/xfrm_input.c x1/net/xfrm/xfrm_input.c
|
|
--- linux-2.6.11/net/xfrm/xfrm_input.c 2005-03-02 08:38:34.000000000 +0100
|
|
+++ x1/net/xfrm/xfrm_input.c 2005-02-18 10:36:26.000000000 +0100
|
|
@@ -46,24 +46,21 @@
|
|
|
|
/* Fetch spi and seq from ipsec header */
|
|
|
|
-int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi, u32 *seq)
|
|
+int xfrm_parse_spi(struct sk_buff *skb, u8 nexthdr, u32 *spi)
|
|
{
|
|
- int offset, offset_seq;
|
|
+ int offset;
|
|
|
|
switch (nexthdr) {
|
|
case IPPROTO_AH:
|
|
offset = offsetof(struct ip_auth_hdr, spi);
|
|
- offset_seq = offsetof(struct ip_auth_hdr, seq_no);
|
|
break;
|
|
case IPPROTO_ESP:
|
|
offset = offsetof(struct ip_esp_hdr, spi);
|
|
- offset_seq = offsetof(struct ip_esp_hdr, seq_no);
|
|
break;
|
|
case IPPROTO_COMP:
|
|
if (!pskb_may_pull(skb, sizeof(struct ip_comp_hdr)))
|
|
return -EINVAL;
|
|
*spi = ntohl(ntohs(*(u16*)(skb->h.raw + 2)));
|
|
- *seq = 0;
|
|
return 0;
|
|
default:
|
|
return 1;
|
|
@@ -73,7 +70,6 @@
|
|
return -EINVAL;
|
|
|
|
*spi = *(u32*)(skb->h.raw + offset);
|
|
- *seq = *(u32*)(skb->h.raw + offset_seq);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(xfrm_parse_spi);
|
|
diff -urN linux-2.6.11/net/xfrm/xfrm_policy.c x1/net/xfrm/xfrm_policy.c
|
|
--- linux-2.6.11/net/xfrm/xfrm_policy.c 2005-03-02 08:38:09.000000000 +0100
|
|
+++ x1/net/xfrm/xfrm_policy.c 2005-02-28 07:45:55.000000000 +0100
|
|
@@ -703,9 +703,14 @@
|
|
|
|
static inline int policy_to_flow_dir(int dir)
|
|
{
|
|
+#ifdef CONFIG_USE_POLICY_FWD
|
|
if (XFRM_POLICY_IN == FLOW_DIR_IN &&
|
|
XFRM_POLICY_OUT == FLOW_DIR_OUT &&
|
|
XFRM_POLICY_FWD == FLOW_DIR_FWD)
|
|
+#else
|
|
+ if (XFRM_POLICY_IN == FLOW_DIR_IN &&
|
|
+ XFRM_POLICY_OUT == FLOW_DIR_OUT)
|
|
+#endif
|
|
return dir;
|
|
switch (dir) {
|
|
default:
|
|
@@ -713,8 +718,10 @@
|
|
return FLOW_DIR_IN;
|
|
case XFRM_POLICY_OUT:
|
|
return FLOW_DIR_OUT;
|
|
+#ifdef CONFIG_USE_POLICY_FWD
|
|
case XFRM_POLICY_FWD:
|
|
return FLOW_DIR_FWD;
|
|
+#endif
|
|
};
|
|
}
|
|
|
|
diff -urN linux-2.6.11/net/xfrm/xfrm_user.c x1/net/xfrm/xfrm_user.c
|
|
--- linux-2.6.11/net/xfrm/xfrm_user.c 2005-03-02 08:38:10.000000000 +0100
|
|
+++ x1/net/xfrm/xfrm_user.c 2005-02-03 06:35:55.000000000 +0100
|
|
@@ -530,7 +530,9 @@
|
|
switch (dir) {
|
|
case XFRM_POLICY_IN:
|
|
case XFRM_POLICY_OUT:
|
|
+#ifdef CONFIG_USE_POLICY_FWD
|
|
case XFRM_POLICY_FWD:
|
|
+#endif
|
|
break;
|
|
|
|
default:
|
|
@@ -1136,14 +1138,14 @@
|
|
|
|
switch (family) {
|
|
case AF_INET:
|
|
- if (opt != IP_XFRM_POLICY) {
|
|
+ if (opt != IP_XFRM_POLICY && opt != IP_IPSEC_POLICY) {
|
|
*dir = -EOPNOTSUPP;
|
|
return NULL;
|
|
}
|
|
break;
|
|
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
|
case AF_INET6:
|
|
- if (opt != IPV6_XFRM_POLICY) {
|
|
+ if (opt != IPV6_XFRM_POLICY && opt != IPV6_IPSEC_POLICY) {
|
|
*dir = -EOPNOTSUPP;
|
|
return NULL;
|
|
}
|