From 84f9307c5da84a7c8513e7607dc8427d2cbd63e3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 30 Nov 2011 19:00:53 +0000 Subject: ipv4: use a 64bit load/store in output path gcc compiler is smart enough to use a single load/store if we memcpy(dptr, sptr, 8) on x86_64, regardless of CONFIG_CC_OPTIMIZE_FOR_SIZE In IP header, daddr immediately follows saddr, this wont change in the future. We only need to make sure our flowi4 (saddr,daddr) fields wont break the rule. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/flow.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'include/net/flow.h') diff --git a/include/net/flow.h b/include/net/flow.h index a09447749e2d..9192d690b562 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -59,8 +59,11 @@ struct flowi4 { #define flowi4_proto __fl_common.flowic_proto #define flowi4_flags __fl_common.flowic_flags #define flowi4_secid __fl_common.flowic_secid - __be32 daddr; + + /* (saddr,daddr) must be grouped, same order as in IP header */ __be32 saddr; + __be32 daddr; + union flowi_uli uli; #define fl4_sport uli.ports.sport #define fl4_dport uli.ports.dport -- cgit v1.2.3-59-g8ed1b From c0ed1c14a72ca9ebacd51fb94a8aca488b0d361e Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Wed, 21 Dec 2011 16:48:08 -0500 Subject: net: Add a flow_cache_flush_deferred function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit flow_cach_flush() might sleep but can be called from atomic context via the xfrm garbage collector. So add a flow_cache_flush_deferred() function and use this if the xfrm garbage colector is invoked from within the packet path. Signed-off-by: Steffen Klassert Acked-by: Timo Teräs Signed-off-by: David S. Miller --- include/net/flow.h | 1 + net/core/flow.c | 12 ++++++++++++ net/xfrm/xfrm_policy.c | 18 ++++++++++++++---- 3 files changed, 27 insertions(+), 4 deletions(-) (limited to 'include/net/flow.h') diff --git a/include/net/flow.h b/include/net/flow.h index a09447749e2d..57f15a7f1cdd 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -207,6 +207,7 @@ extern struct flow_cache_object *flow_cache_lookup( u8 dir, flow_resolve_t resolver, void *ctx); extern void flow_cache_flush(void); +extern void flow_cache_flush_deferred(void); extern atomic_t flow_cache_genid; #endif diff --git a/net/core/flow.c b/net/core/flow.c index 8ae42de9c79e..e318c7e98042 100644 --- a/net/core/flow.c +++ b/net/core/flow.c @@ -358,6 +358,18 @@ void flow_cache_flush(void) put_online_cpus(); } +static void flow_cache_flush_task(struct work_struct *work) +{ + flow_cache_flush(); +} + +static DECLARE_WORK(flow_cache_flush_work, flow_cache_flush_task); + +void flow_cache_flush_deferred(void) +{ + schedule_work(&flow_cache_flush_work); +} + static int __cpuinit flow_cache_cpu_prepare(struct flow_cache *fc, int cpu) { struct flow_cache_percpu *fcp = per_cpu_ptr(fc->percpu, cpu); diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 2118d6446630..9049a5caeb25 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -2276,8 +2276,6 @@ static void __xfrm_garbage_collect(struct net *net) { struct dst_entry *head, *next; - flow_cache_flush(); - spin_lock_bh(&xfrm_policy_sk_bundle_lock); head = xfrm_policy_sk_bundles; xfrm_policy_sk_bundles = NULL; @@ -2290,6 +2288,18 @@ static void __xfrm_garbage_collect(struct net *net) } } +static void xfrm_garbage_collect(struct net *net) +{ + flow_cache_flush(); + __xfrm_garbage_collect(net); +} + +static void xfrm_garbage_collect_deferred(struct net *net) +{ + flow_cache_flush_deferred(); + __xfrm_garbage_collect(net); +} + static void xfrm_init_pmtu(struct dst_entry *dst) { do { @@ -2422,7 +2432,7 @@ int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo) if (likely(dst_ops->neigh_lookup == NULL)) dst_ops->neigh_lookup = xfrm_neigh_lookup; if (likely(afinfo->garbage_collect == NULL)) - afinfo->garbage_collect = __xfrm_garbage_collect; + afinfo->garbage_collect = xfrm_garbage_collect_deferred; xfrm_policy_afinfo[afinfo->family] = afinfo; } write_unlock_bh(&xfrm_policy_afinfo_lock); @@ -2516,7 +2526,7 @@ static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void switch (event) { case NETDEV_DOWN: - __xfrm_garbage_collect(dev_net(dev)); + xfrm_garbage_collect(dev_net(dev)); } return NOTIFY_DONE; } -- cgit v1.2.3-59-g8ed1b From 747465ef7a082033e086dedc8189febfda43b015 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 16 Jan 2012 19:27:39 +0000 Subject: net: fix some sparse errors make C=2 CF="-D__CHECK_ENDIAN__" M=net And fix flowi4_init_output() prototype for sport Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/flow.h | 2 +- net/core/secure_seq.c | 2 +- net/ipv4/inetpeer.c | 2 +- net/ipv4/ipconfig.c | 8 ++++---- net/ipv4/ping.c | 27 ++++++++++++++------------- net/ipv6/datagram.c | 2 +- 6 files changed, 22 insertions(+), 21 deletions(-) (limited to 'include/net/flow.h') diff --git a/include/net/flow.h b/include/net/flow.h index da1f064a81b3..9b582437fbea 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -78,7 +78,7 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, __u32 mark, __u8 tos, __u8 scope, __u8 proto, __u8 flags, __be32 daddr, __be32 saddr, - __be16 dport, __be32 sport) + __be16 dport, __be16 sport) { fl4->flowi4_oif = oif; fl4->flowi4_iif = 0; diff --git a/net/core/secure_seq.c b/net/core/secure_seq.c index 6fd44606fdd1..99b2596531bb 100644 --- a/net/core/secure_seq.c +++ b/net/core/secure_seq.c @@ -46,7 +46,7 @@ __u32 secure_tcpv6_sequence_number(const __be32 *saddr, const __be32 *daddr, memcpy(hash, saddr, 16); for (i = 0; i < 4; i++) - secret[i] = net_secret[i] + daddr[i]; + secret[i] = net_secret[i] + (__force u32)daddr[i]; secret[4] = net_secret[4] + (((__force u16)sport << 16) + (__force u16)dport); for (i = 5; i < MD5_MESSAGE_BYTES / 4; i++) diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index 86f13c67ea85..986a02bda660 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -136,7 +136,7 @@ static int addr_compare(const struct inetpeer_addr *a, for (i = 0; i < n; i++) { if (a->addr.a6[i] == b->addr.a6[i]) continue; - if (a->addr.a6[i] < b->addr.a6[i]) + if ((__force u32)a->addr.a6[i] < (__force u32)b->addr.a6[i]) return -1; return 1; } diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c index 7e4ec9fc2cef..6e412a60a91f 100644 --- a/net/ipv4/ipconfig.c +++ b/net/ipv4/ipconfig.c @@ -141,7 +141,7 @@ __be32 ic_servaddr = NONE; /* Boot server IP address */ __be32 root_server_addr = NONE; /* Address of NFS server */ u8 root_server_path[256] = { 0, }; /* Path to mount as root */ -u32 ic_dev_xid; /* Device under configuration */ +__be32 ic_dev_xid; /* Device under configuration */ /* vendor class identifier */ static char vendor_class_identifier[253] __initdata; @@ -859,9 +859,9 @@ static int __init ic_bootp_string(char *dest, char *src, int len, int max) */ static void __init ic_do_bootp_ext(u8 *ext) { - u8 servers; - int i; - u16 mtu; + u8 servers; + int i; + __be16 mtu; #ifdef IPCONFIG_DEBUG u8 *c; diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c index 43d4c3b22369..aea5a199c37a 100644 --- a/net/ipv4/ping.c +++ b/net/ipv4/ping.c @@ -140,13 +140,14 @@ static void ping_v4_unhash(struct sock *sk) write_lock_bh(&ping_table.lock); hlist_nulls_del(&sk->sk_nulls_node); sock_put(sk); - isk->inet_num = isk->inet_sport = 0; + isk->inet_num = 0; + isk->inet_sport = 0; sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1); write_unlock_bh(&ping_table.lock); } } -static struct sock *ping_v4_lookup(struct net *net, u32 saddr, u32 daddr, +static struct sock *ping_v4_lookup(struct net *net, __be32 saddr, __be32 daddr, u16 ident, int dif) { struct hlist_nulls_head *hslot = ping_hashslot(&ping_table, net, ident); @@ -154,15 +155,15 @@ static struct sock *ping_v4_lookup(struct net *net, u32 saddr, u32 daddr, struct inet_sock *isk; struct hlist_nulls_node *hnode; - pr_debug("try to find: num = %d, daddr = %ld, dif = %d\n", - (int)ident, (unsigned long)daddr, dif); + pr_debug("try to find: num = %d, daddr = %pI4, dif = %d\n", + (int)ident, &daddr, dif); read_lock_bh(&ping_table.lock); ping_portaddr_for_each_entry(sk, hnode, hslot) { isk = inet_sk(sk); - pr_debug("found: %p: num = %d, daddr = %ld, dif = %d\n", sk, - (int)isk->inet_num, (unsigned long)isk->inet_rcv_saddr, + pr_debug("found: %p: num = %d, daddr = %pI4, dif = %d\n", sk, + (int)isk->inet_num, &isk->inet_rcv_saddr, sk->sk_bound_dev_if); pr_debug("iterate\n"); @@ -254,7 +255,7 @@ static int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) sk, addr->sin_addr.s_addr, ntohs(addr->sin_port)); chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr); - if (addr->sin_addr.s_addr == INADDR_ANY) + if (addr->sin_addr.s_addr == htonl(INADDR_ANY)) chk_addr_ret = RTN_LOCAL; if ((sysctl_ip_nonlocal_bind == 0 && @@ -278,9 +279,9 @@ static int ping_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) goto out; } - pr_debug("after bind(): num = %d, daddr = %ld, dif = %d\n", + pr_debug("after bind(): num = %d, daddr = %pI4, dif = %d\n", (int)isk->inet_num, - (unsigned long) isk->inet_rcv_saddr, + &isk->inet_rcv_saddr, (int)sk->sk_bound_dev_if); err = 0; @@ -407,7 +408,7 @@ out: struct pingfakehdr { struct icmphdr icmph; struct iovec *iov; - u32 wcheck; + __wsum wcheck; }; static int ping_getfrag(void *from, char * to, @@ -459,7 +460,7 @@ static int ping_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, struct rtable *rt = NULL; struct ip_options_data opt_copy; int free = 0; - u32 saddr, daddr, faddr; + __be32 saddr, daddr, faddr; u8 tos; int err; @@ -696,8 +697,8 @@ void ping_rcv(struct sk_buff *skb) struct net *net = dev_net(skb->dev); struct iphdr *iph = ip_hdr(skb); struct icmphdr *icmph = icmp_hdr(skb); - u32 saddr = iph->saddr; - u32 daddr = iph->daddr; + __be32 saddr = iph->saddr; + __be32 daddr = iph->daddr; /* We assume the packet has already been checked by icmp_rcv */ diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index ae08aee1773c..251e7cd75e89 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -575,7 +575,7 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) } if (np->rxopt.bits.rxorigdstaddr) { struct sockaddr_in6 sin6; - u16 *ports = (u16 *) skb_transport_header(skb); + __be16 *ports = (__be16 *) skb_transport_header(skb); if (skb_transport_offset(skb) + 4 <= skb->len) { /* All current transport protocols have the port numbers in the -- cgit v1.2.3-59-g8ed1b From e6b45241c57a83197e5de9166b3b0d32ac562609 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Sat, 4 Feb 2012 13:04:46 +0000 Subject: ipv4: reset flowi parameters on route connect Eric Dumazet found that commit 813b3b5db83 (ipv4: Use caller's on-stack flowi as-is in output route lookups.) that comes in 3.0 added a regression. The problem appears to be that resulting flowi4_oif is used incorrectly as input parameter to some routing lookups. The result is that when connecting to local port without listener if the IP address that is used is not on a loopback interface we incorrectly assign RTN_UNICAST to the output route because no route is matched by oif=lo. The RST packet can not be sent immediately by tcp_v4_send_reset because it expects RTN_LOCAL. So, change ip_route_connect and ip_route_newports to update the flowi4 fields that are input parameters because we do not want unnecessary binding to oif. To make it clear what are the input parameters that can be modified during lookup and to show which fields of floiw4 are reused add a new function to update the flowi4 structure: flowi4_update_output. Thanks to Yurij M. Plotnikov for providing a bug report including a program to reproduce the problem. Thanks to Eric Dumazet for tracking the problem down to tcp_v4_send_reset and providing initial fix. Reported-by: Yurij M. Plotnikov Signed-off-by: Julian Anastasov Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/flow.h | 10 ++++++++++ include/net/route.h | 4 ++++ 2 files changed, 14 insertions(+) (limited to 'include/net/flow.h') diff --git a/include/net/flow.h b/include/net/flow.h index 9b582437fbea..6c469dbdb917 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -93,6 +93,16 @@ static inline void flowi4_init_output(struct flowi4 *fl4, int oif, fl4->fl4_dport = dport; fl4->fl4_sport = sport; } + +/* Reset some input parameters after previous lookup */ +static inline void flowi4_update_output(struct flowi4 *fl4, int oif, __u8 tos, + __be32 daddr, __be32 saddr) +{ + fl4->flowi4_oif = oif; + fl4->flowi4_tos = tos; + fl4->daddr = daddr; + fl4->saddr = saddr; +} struct flowi6 { diff --git a/include/net/route.h b/include/net/route.h index 91855d185b53..b1c0d5b564c2 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -270,6 +270,7 @@ static inline struct rtable *ip_route_connect(struct flowi4 *fl4, if (IS_ERR(rt)) return rt; ip_rt_put(rt); + flowi4_update_output(fl4, oif, tos, fl4->daddr, fl4->saddr); } security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); return ip_route_output_flow(net, fl4, sk); @@ -284,6 +285,9 @@ static inline struct rtable *ip_route_newports(struct flowi4 *fl4, struct rtable fl4->fl4_dport = dport; fl4->fl4_sport = sport; ip_rt_put(rt); + flowi4_update_output(fl4, sk->sk_bound_dev_if, + RT_CONN_FLAGS(sk), fl4->daddr, + fl4->saddr); security_sk_classify_flow(sk, flowi4_to_flowi(fl4)); return ip_route_output_flow(sock_net(sk), fl4, sk); } -- cgit v1.2.3-59-g8ed1b