diff options
| -rw-r--r-- | include/linux/netfilter_bridge.h | 5 | ||||
| -rw-r--r-- | include/linux/tcp.h | 5 | ||||
| -rw-r--r-- | include/net/sctp/sctp.h | 1 | ||||
| -rw-r--r-- | include/net/sock.h | 19 | ||||
| -rw-r--r-- | include/net/xfrm.h | 2 | ||||
| -rw-r--r-- | net/appletalk/sysctl_net_atalk.c | 3 | ||||
| -rw-r--r-- | net/bridge/br_netfilter.c | 40 | ||||
| -rw-r--r-- | net/core/dev.c | 2 | ||||
| -rw-r--r-- | net/core/neighbour.c | 4 | ||||
| -rw-r--r-- | net/core/sock.c | 25 | ||||
| -rw-r--r-- | net/core/sysctl_net_core.c | 6 | ||||
| -rw-r--r-- | net/ipv4/netfilter/ipt_recent.c | 4 | ||||
| -rw-r--r-- | net/ipv6/addrconf.c | 2 | ||||
| -rw-r--r-- | net/ipv6/ndisc.c | 39 | ||||
| -rw-r--r-- | net/sctp/associola.c | 1 | ||||
| -rw-r--r-- | net/sctp/outqueue.c | 1 | ||||
| -rw-r--r-- | net/sctp/socket.c | 1 | ||||
| -rw-r--r-- | net/sctp/transport.c | 1 | ||||
| -rw-r--r-- | net/xfrm/xfrm_policy.c | 85 | ||||
| -rw-r--r-- | net/xfrm/xfrm_state.c | 2 |
20 files changed, 149 insertions, 99 deletions
diff --git a/include/linux/netfilter_bridge.h b/include/linux/netfilter_bridge.h index 88cc98af2dce..a6531bb097ca 100644 --- a/include/linux/netfilter_bridge.h +++ b/include/linux/netfilter_bridge.h @@ -71,12 +71,10 @@ static inline void nf_bridge_maybe_copy_header(struct sk_buff *skb) { if (skb->nf_bridge) { -#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) if (skb->protocol == __constant_htons(ETH_P_8021Q)) { memcpy(skb->data - 18, skb->nf_bridge->hh, 18); skb_push(skb, 4); } else -#endif memcpy(skb->data - 16, skb->nf_bridge->hh, 16); } } @@ -86,10 +84,9 @@ void nf_bridge_save_header(struct sk_buff *skb) { int header_size = 16; -#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) if (skb->protocol == __constant_htons(ETH_P_8021Q)) header_size = 18; -#endif + memcpy(skb->nf_bridge->hh, skb->data - header_size, header_size); } diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 3a2e7bf3c44a..d25e5bd21c4d 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -386,7 +386,10 @@ struct tcp_sock { struct tcp_opt tcp; }; -#define tcp_sk(__sk) (&((struct tcp_sock *)__sk)->tcp) +static inline struct tcp_opt * tcp_sk(const struct sock *__sk) +{ + return &((struct tcp_sock *)__sk)->tcp; +} #endif diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index bf33562cd101..7edb15d6561a 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -90,7 +90,6 @@ #include <net/snmp.h> #include <net/sctp/structs.h> #include <net/sctp/constants.h> -#include <net/sctp/sm.h> /* Set SCTP_DEBUG flag via config if not already set. */ diff --git a/include/net/sock.h b/include/net/sock.h index ac2eccb7ff9a..1d85a3e5a3cc 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -542,24 +542,9 @@ static inline struct inode *SOCK_INODE(struct socket *socket) extern void __lock_sock(struct sock *sk); extern void __release_sock(struct sock *sk); #define sock_owned_by_user(sk) ((sk)->sk_lock.owner) -#define lock_sock(__sk) \ -do { might_sleep(); \ - spin_lock_bh(&((__sk)->sk_lock.slock)); \ - if ((__sk)->sk_lock.owner) \ - __lock_sock(__sk); \ - (__sk)->sk_lock.owner = (void *)1; \ - spin_unlock_bh(&((__sk)->sk_lock.slock)); \ -} while(0) -#define release_sock(__sk) \ -do { spin_lock_bh(&((__sk)->sk_lock.slock)); \ - if ((__sk)->sk_backlog.tail) \ - __release_sock(__sk); \ - (__sk)->sk_lock.owner = NULL; \ - if (waitqueue_active(&((__sk)->sk_lock.wq))) \ - wake_up(&((__sk)->sk_lock.wq)); \ - spin_unlock_bh(&((__sk)->sk_lock.slock)); \ -} while(0) +extern void lock_sock(struct sock *sk); +extern void release_sock(struct sock *sk); /* BH context may only use the following locking interface. */ #define bh_lock_sock(__sk) spin_lock(&((__sk)->sk_lock.slock)) diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 2ae391720d4d..67072071aec1 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -860,7 +860,7 @@ extern void xfrm_policy_flush(void); extern void xfrm_policy_kill(struct xfrm_policy *); extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol); extern struct xfrm_policy *xfrm_sk_policy_lookup(struct sock *sk, int dir, struct flowi *fl); -extern int xfrm_flush_bundles(struct xfrm_state *x); +extern int xfrm_flush_bundles(void); extern wait_queue_head_t km_waitq; extern void km_state_expired(struct xfrm_state *x, int hard); diff --git a/net/appletalk/sysctl_net_atalk.c b/net/appletalk/sysctl_net_atalk.c index edddd3291f70..25b33f670499 100644 --- a/net/appletalk/sysctl_net_atalk.c +++ b/net/appletalk/sysctl_net_atalk.c @@ -23,6 +23,7 @@ static struct ctl_table atalk_table[] = { .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_jiffies, + .strategy = &sysctl_jiffies, }, { .ctl_name = NET_ATALK_AARP_TICK_TIME, @@ -31,6 +32,7 @@ static struct ctl_table atalk_table[] = { .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_jiffies, + .strategy = &sysctl_jiffies, }, { .ctl_name = NET_ATALK_AARP_RETRANSMIT_LIMIT, @@ -47,6 +49,7 @@ static struct ctl_table atalk_table[] = { .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_jiffies, + .strategy = &sysctl_jiffies, }, { 0 }, }; diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 932f3ae2bbea..359555cd35b6 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -356,6 +356,7 @@ static unsigned int br_nf_local_in(unsigned int hook, struct sk_buff **pskb, return NF_ACCEPT; } + /* PF_BRIDGE/FORWARD *************************************************/ static int br_nf_forward_finish(struct sk_buff *skb) { @@ -466,6 +467,7 @@ static unsigned int br_nf_forward_arp(unsigned int hook, struct sk_buff **pskb, return NF_STOLEN; } + /* PF_BRIDGE/LOCAL_OUT ***********************************************/ static int br_nf_local_out_finish(struct sk_buff *skb) { @@ -531,9 +533,7 @@ static unsigned int br_nf_local_out(unsigned int hook, struct sk_buff **pskb, return NF_ACCEPT; nf_bridge = skb->nf_bridge; - nf_bridge->physoutdev = skb->dev; - realindev = nf_bridge->physindev; /* Bridged, take PF_BRIDGE/FORWARD. @@ -601,18 +601,15 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb, struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)(skb->mac.ethernet); struct net_device *realoutdev = bridge_parent(skb->dev); - /* Be very paranoid. Must be a device driver bug. */ +#ifdef CONFIG_NETFILTER_DEBUG + /* Be very paranoid. This probably won't happen anymore, but let's + * keep the check just to be sure... */ if (skb->mac.raw < skb->head || skb->mac.raw + ETH_HLEN > skb->data) { printk(KERN_CRIT "br_netfilter: Argh!! br_nf_post_routing: " "bad mac.raw pointer."); - if (skb->dev != NULL) { - printk("[%s]", skb->dev->name); - if (has_bridge_parent(skb->dev)) - printk("[%s]", bridge_parent(skb->dev)->name); - } - printk(" head:%p, raw:%p\n", skb->head, skb->mac.raw); - return NF_ACCEPT; + goto print_error; } +#endif #ifdef CONFIG_SYSCTL if (!nf_bridge) @@ -622,13 +619,16 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb, if (skb->protocol != __constant_htons(ETH_P_IP) && !IS_VLAN_IP) return NF_ACCEPT; +#ifdef CONFIG_NETFILTER_DEBUG /* Sometimes we get packets with NULL ->dst here (for example, - * running a dhcp client daemon triggers this). + * running a dhcp client daemon triggers this). This should now + * be fixed, but let's keep the check around. */ - if (skb->dst == NULL) - return NF_ACCEPT; + if (skb->dst == NULL) { + printk(KERN_CRIT "br_netfilter: skb->dst == NULL."); + goto print_error; + } -#ifdef CONFIG_NETFILTER_DEBUG skb->nf_debug ^= (1 << NF_IP_POST_ROUTING); #endif @@ -655,6 +655,18 @@ static unsigned int br_nf_post_routing(unsigned int hook, struct sk_buff **pskb, realoutdev, br_dev_queue_push_xmit); return NF_STOLEN; + +#ifdef CONFIG_NETFILTER_DEBUG +print_error: + if (skb->dev != NULL) { + printk("[%s]", skb->dev->name); + if (has_bridge_parent(skb->dev)) + printk("[%s]", bridge_parent(skb->dev)->name); + } + printk(" head:%p, raw:%p, data:%p\n", skb->head, skb->mac.raw, + skb->data); + return NF_ACCEPT; +#endif } diff --git a/net/core/dev.c b/net/core/dev.c index 47fff21ea3b2..30cba1e1c633 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1543,7 +1543,7 @@ static inline int __handle_bridge(struct sk_buff *skb, struct packet_type **pt_prev, int *ret) { #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE) - if (skb->dev->br_port) { + if (skb->dev->br_port && skb->pkt_type != PACKET_LOOPBACK) { *ret = handle_bridge(skb, *pt_prev); if (br_handle_frame_hook(skb) == 0) return 1; diff --git a/net/core/neighbour.c b/net/core/neighbour.c index 247b6d6e661d..4b194b6be05a 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -1518,6 +1518,7 @@ struct neigh_sysctl_table { .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_jiffies, + .strategy = &sysctl_jiffies, }, { .ctl_name = NET_NEIGH_DELAY_PROBE_TIME, @@ -1525,6 +1526,7 @@ struct neigh_sysctl_table { .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_jiffies, + .strategy = &sysctl_jiffies, }, { .ctl_name = NET_NEIGH_GC_STALE_TIME, @@ -1532,6 +1534,7 @@ struct neigh_sysctl_table { .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_jiffies, + .strategy = &sysctl_jiffies, }, { .ctl_name = NET_NEIGH_UNRES_QLEN, @@ -1574,6 +1577,7 @@ struct neigh_sysctl_table { .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_jiffies, + .strategy = &sysctl_jiffies, }, { .ctl_name = NET_NEIGH_GC_THRESH1, diff --git a/net/core/sock.c b/net/core/sock.c index 9d51516eaa56..d549d519482d 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1119,6 +1119,31 @@ void sock_init_data(struct socket *sock, struct sock *sk) atomic_set(&sk->sk_refcnt, 1); } +void lock_sock(struct sock *sk) +{ + might_sleep(); + spin_lock_bh(&(sk->sk_lock.slock)); + if (sk->sk_lock.owner) + __lock_sock(sk); + sk->sk_lock.owner = (void *)1; + spin_unlock_bh(&(sk->sk_lock.slock)); +} + +EXPORT_SYMBOL(lock_sock); + +void release_sock(struct sock *sk) +{ + spin_lock_bh(&(sk->sk_lock.slock)); + if (sk->sk_backlog.tail) + __release_sock(sk); + sk->sk_lock.owner = NULL; + if (waitqueue_active(&(sk->sk_lock.wq))) + wake_up(&(sk->sk_lock.wq)); + spin_unlock_bh(&(sk->sk_lock.slock)); +} + +EXPORT_SYMBOL(release_sock); + EXPORT_SYMBOL(__lock_sock); EXPORT_SYMBOL(__release_sock); EXPORT_SYMBOL(sk_alloc); diff --git a/net/core/sysctl_net_core.c b/net/core/sysctl_net_core.c index 46c0c78b196d..642ef77a2824 100644 --- a/net/core/sysctl_net_core.c +++ b/net/core/sysctl_net_core.c @@ -146,7 +146,8 @@ ctl_table core_table[] = { .data = &net_msg_cost, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec_jiffies + .proc_handler = &proc_dointvec_jiffies, + .strategy = &sysctl_jiffies, }, { .ctl_name = NET_CORE_MSG_BURST, @@ -154,7 +155,8 @@ ctl_table core_table[] = { .data = &net_msg_burst, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = &proc_dointvec_jiffies + .proc_handler = &proc_dointvec_jiffies, + .strategy = &sysctl_jiffies, }, { .ctl_name = NET_CORE_OPTMEM_MAX, diff --git a/net/ipv4/netfilter/ipt_recent.c b/net/ipv4/netfilter/ipt_recent.c index 3e03fe295529..eda86a22accd 100644 --- a/net/ipv4/netfilter/ipt_recent.c +++ b/net/ipv4/netfilter/ipt_recent.c @@ -91,8 +91,10 @@ static struct recent_ip_tables *r_tables = NULL; */ static spinlock_t recent_lock = SPIN_LOCK_UNLOCKED; +#ifdef CONFIG_PROC_FS /* Our /proc/net/ipt_recent entry */ static struct proc_dir_entry *proc_net_ipt_recent = NULL; +#endif /* Function declaration for later. */ static int @@ -959,8 +961,10 @@ static int __init init(void) int count; printk(version); +#ifdef CONFIG_PROC_FS proc_net_ipt_recent = proc_mkdir("ipt_recent",proc_net); if(!proc_net_ipt_recent) return -ENOMEM; +#endif if(ip_list_hash_size && ip_list_hash_size <= ip_list_tot) { printk(KERN_WARNING RECENT_NAME ": ip_list_hash_size too small, resetting to default.\n"); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index b08ba41c5356..0d4b1688a801 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -2946,6 +2946,7 @@ static struct addrconf_sysctl_table .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_jiffies, + .strategy = &sysctl_jiffies, }, { .ctl_name = NET_IPV6_RTR_SOLICIT_DELAY, @@ -2954,6 +2955,7 @@ static struct addrconf_sysctl_table .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_jiffies, + .strategy = &sysctl_jiffies, }, #ifdef CONFIG_IPV6_PRIVACY { diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 0309a24a367e..f2e7c8bb7fe9 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -75,6 +75,9 @@ #include <net/checksum.h> #include <linux/proc_fs.h> +#include <linux/netfilter.h> +#include <linux/netfilter_ipv6.h> + static struct socket *ndisc_socket; static u32 ndisc_hash(const void *pkey, const struct net_device *dev); @@ -497,10 +500,11 @@ static void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, skb->dst = dst; idev = in6_dev_get(dst->dev); - dst_output(skb); - - ICMP6_INC_STATS(idev, Icmp6OutNeighborAdvertisements); - ICMP6_INC_STATS(idev, Icmp6OutMsgs); + err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output); + if (!err) { + ICMP6_INC_STATS(idev, Icmp6OutNeighborAdvertisements); + ICMP6_INC_STATS(idev, Icmp6OutMsgs); + } if (likely(idev != NULL)) in6_dev_put(idev); @@ -576,10 +580,11 @@ void ndisc_send_ns(struct net_device *dev, struct neighbour *neigh, /* send it! */ skb->dst = dst; idev = in6_dev_get(dst->dev); - dst_output(skb); - - ICMP6_INC_STATS(idev, Icmp6OutNeighborSolicits); - ICMP6_INC_STATS(idev, Icmp6OutMsgs); + err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output); + if (!err) { + ICMP6_INC_STATS(idev, Icmp6OutNeighborSolicits); + ICMP6_INC_STATS(idev, Icmp6OutMsgs); + } if (likely(idev != NULL)) in6_dev_put(idev); @@ -644,10 +649,11 @@ void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr, /* send it! */ skb->dst = dst; idev = in6_dev_get(dst->dev); - dst_output(skb); - - ICMP6_INC_STATS(idev, Icmp6OutRouterSolicits); - ICMP6_INC_STATS(idev, Icmp6OutMsgs); + err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output); + if (!err) { + ICMP6_INC_STATS(idev, Icmp6OutRouterSolicits); + ICMP6_INC_STATS(idev, Icmp6OutMsgs); + } if (likely(idev != NULL)) in6_dev_put(idev); @@ -1404,10 +1410,11 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, buff->dst = dst; idev = in6_dev_get(dst->dev); - dst_output(buff); - - ICMP6_INC_STATS(idev, Icmp6OutRedirects); - ICMP6_INC_STATS(idev, Icmp6OutMsgs); + err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, buff, NULL, dst->dev, dst_output); + if (!err) { + ICMP6_INC_STATS(idev, Icmp6OutRedirects); + ICMP6_INC_STATS(idev, Icmp6OutMsgs); + } if (likely(idev != NULL)) in6_dev_put(idev); diff --git a/net/sctp/associola.c b/net/sctp/associola.c index dba3d149e499..44b30efb17a9 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -58,6 +58,7 @@ #include <linux/in.h> #include <net/ipv6.h> #include <net/sctp/sctp.h> +#include <net/sctp/sm.h> /* Forward declarations for internal functions. */ static void sctp_assoc_bh_rcv(struct sctp_association *asoc); diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index d26292af9ff1..dc4cb1a5a739 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -53,6 +53,7 @@ #include <net/sock.h> /* For skb_set_owner_w */ #include <net/sctp/sctp.h> +#include <net/sctp/sm.h> /* Declare internal functions here. */ static int sctp_acked(struct sctp_sackhdr *sack, __u32 tsn); diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 0ac8c9bf5363..36b2ec11a3a8 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -77,6 +77,7 @@ #include <linux/socket.h> /* for sa_family_t */ #include <net/sock.h> #include <net/sctp/sctp.h> +#include <net/sctp/sm.h> /* WARNING: Please do not remove the SCTP_STATIC attribute to * any of the functions below as they are used to export functions diff --git a/net/sctp/transport.c b/net/sctp/transport.c index 485d77015399..026ffebb61d4 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -50,6 +50,7 @@ #include <linux/types.h> #include <net/sctp/sctp.h> +#include <net/sctp/sm.h> /* 1st Level Abstractions. */ diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 85f626d20c4a..0f970906c9ef 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -19,6 +19,8 @@ #include <linux/list.h> #include <linux/spinlock.h> #include <linux/workqueue.h> +#include <linux/notifier.h> +#include <linux/netdevice.h> #include <net/xfrm.h> #include <net/ip.h> @@ -690,6 +692,8 @@ static inline int policy_to_flow_dir(int dir) }; } +static int stale_bundle(struct dst_entry *dst); + /* Main function: finds/creates a bundle for given flow. * * At the moment we eat a raw IP route. Mostly to speed up lookups @@ -814,10 +818,11 @@ restart: } write_lock_bh(&policy->lock); - if (unlikely(policy->dead)) { + if (unlikely(policy->dead || stale_bundle(dst))) { /* Wow! While we worked on resolving, this * policy has gone. Retry. It is not paranoia, * we just cannot enlist new bundle to dead object. + * We can't enlist stable bundles either. */ write_unlock_bh(&policy->lock); @@ -985,18 +990,27 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family) static struct dst_entry *xfrm_dst_check(struct dst_entry *dst, u32 cookie) { + if (!stale_bundle(dst)) + return dst; + + dst_release(dst); + return NULL; +} + +static int stale_bundle(struct dst_entry *dst) +{ struct dst_entry *child = dst; while (child) { if (child->obsolete > 0 || + (child->dev && !netif_running(child->dev)) || (child->xfrm && child->xfrm->km.state != XFRM_STATE_VALID)) { - dst_release(dst); - return NULL; + return 1; } child = child->child; } - return dst; + return 0; } static void xfrm_dst_destroy(struct dst_entry *dst) @@ -1022,7 +1036,7 @@ static struct dst_entry *xfrm_negative_advice(struct dst_entry *dst) return dst; } -static void __xfrm_garbage_collect(void) +static void xfrm_prune_bundles(int (*func)(struct dst_entry *)) { int i; struct xfrm_policy *pol; @@ -1034,7 +1048,7 @@ static void __xfrm_garbage_collect(void) write_lock(&pol->lock); dstp = &pol->bundles; while ((dst=*dstp) != NULL) { - if (atomic_read(&dst->__refcnt) == 0) { + if (func(dst)) { *dstp = dst->next; dst->next = gc_list; gc_list = dst; @@ -1054,46 +1068,19 @@ static void __xfrm_garbage_collect(void) } } -static int bundle_depends_on(struct dst_entry *dst, struct xfrm_state *x) +static int unused_bundle(struct dst_entry *dst) { - do { - if (dst->xfrm == x) - return 1; - } while ((dst = dst->child) != NULL); - return 0; + return !atomic_read(&dst->__refcnt); } -int xfrm_flush_bundles(struct xfrm_state *x) +static void __xfrm_garbage_collect(void) { - int i; - struct xfrm_policy *pol; - struct dst_entry *dst, **dstp, *gc_list = NULL; - - read_lock_bh(&xfrm_policy_lock); - for (i=0; i<2*XFRM_POLICY_MAX; i++) { - for (pol = xfrm_policy_list[i]; pol; pol = pol->next) { - write_lock(&pol->lock); - dstp = &pol->bundles; - while ((dst=*dstp) != NULL) { - if (bundle_depends_on(dst, x)) { - *dstp = dst->next; - dst->next = gc_list; - gc_list = dst; - } else { - dstp = &dst->next; - } - } - write_unlock(&pol->lock); - } - } - read_unlock_bh(&xfrm_policy_lock); - - while (gc_list) { - dst = gc_list; - gc_list = dst->next; - dst_free(dst); - } + xfrm_prune_bundles(unused_bundle); +} +int xfrm_flush_bundles(void) +{ + xfrm_prune_bundles(stale_bundle); return 0; } @@ -1216,6 +1203,21 @@ void xfrm_policy_put_afinfo(struct xfrm_policy_afinfo *afinfo) read_unlock(&afinfo->lock); } +static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr) +{ + switch (event) { + case NETDEV_DOWN: + xfrm_flush_bundles(); + } + return NOTIFY_DONE; +} + +struct notifier_block xfrm_dev_notifier = { + xfrm_dev_event, + NULL, + 0 +}; + void __init xfrm_policy_init(void) { xfrm_dst_cache = kmem_cache_create("xfrm_dst_cache", @@ -1226,6 +1228,7 @@ void __init xfrm_policy_init(void) panic("XFRM: failed to allocate xfrm_dst_cache\n"); INIT_WORK(&xfrm_policy_gc_work, xfrm_policy_gc_task, NULL); + register_netdevice_notifier(&xfrm_dev_notifier); } void __init xfrm_init(void) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index ab1c1ec1aa12..dc70df79f9cb 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -219,7 +219,7 @@ static void __xfrm_state_delete(struct xfrm_state *x) * there are DSTs attached to this xfrm_state. */ if (atomic_read(&x->refcnt) > 2) - xfrm_flush_bundles(x); + xfrm_flush_bundles(); /* All xfrm_state objects are created by one of two possible * paths: |
