aboutsummaryrefslogtreecommitdiffstats
path: root/net/bridge/br_input.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/bridge/br_input.c')
-rw-r--r--net/bridge/br_input.c24
1 files changed, 15 insertions, 9 deletions
diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c
index d36e700f7a26..826cd5221536 100644
--- a/net/bridge/br_input.c
+++ b/net/bridge/br_input.c
@@ -27,8 +27,10 @@ static int br_pass_frame_up(struct sk_buff *skb)
27 struct net_bridge *br = netdev_priv(brdev); 27 struct net_bridge *br = netdev_priv(brdev);
28 struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats); 28 struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats);
29 29
30 u64_stats_update_begin(&brstats->syncp);
30 brstats->rx_packets++; 31 brstats->rx_packets++;
31 brstats->rx_bytes += skb->len; 32 brstats->rx_bytes += skb->len;
33 u64_stats_update_end(&brstats->syncp);
32 34
33 indev = skb->dev; 35 indev = skb->dev;
34 skb->dev = brdev; 36 skb->dev = brdev;
@@ -37,11 +39,11 @@ static int br_pass_frame_up(struct sk_buff *skb)
37 netif_receive_skb); 39 netif_receive_skb);
38} 40}
39 41
40/* note: already called with rcu_read_lock (preempt_disabled) */ 42/* note: already called with rcu_read_lock */
41int br_handle_frame_finish(struct sk_buff *skb) 43int br_handle_frame_finish(struct sk_buff *skb)
42{ 44{
43 const unsigned char *dest = eth_hdr(skb)->h_dest; 45 const unsigned char *dest = eth_hdr(skb)->h_dest;
44 struct net_bridge_port *p = rcu_dereference(skb->dev->br_port); 46 struct net_bridge_port *p = br_port_get_rcu(skb->dev);
45 struct net_bridge *br; 47 struct net_bridge *br;
46 struct net_bridge_fdb_entry *dst; 48 struct net_bridge_fdb_entry *dst;
47 struct net_bridge_mdb_entry *mdst; 49 struct net_bridge_mdb_entry *mdst;
@@ -108,13 +110,12 @@ drop:
108 goto out; 110 goto out;
109} 111}
110 112
111/* note: already called with rcu_read_lock (preempt_disabled) */ 113/* note: already called with rcu_read_lock */
112static int br_handle_local_finish(struct sk_buff *skb) 114static int br_handle_local_finish(struct sk_buff *skb)
113{ 115{
114 struct net_bridge_port *p = rcu_dereference(skb->dev->br_port); 116 struct net_bridge_port *p = br_port_get_rcu(skb->dev);
115 117
116 if (p) 118 br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
117 br_fdb_update(p->br, p, eth_hdr(skb)->h_source);
118 return 0; /* process further */ 119 return 0; /* process further */
119} 120}
120 121
@@ -131,15 +132,18 @@ static inline int is_link_local(const unsigned char *dest)
131} 132}
132 133
133/* 134/*
134 * Called via br_handle_frame_hook.
135 * Return NULL if skb is handled 135 * Return NULL if skb is handled
136 * note: already called with rcu_read_lock (preempt_disabled) 136 * note: already called with rcu_read_lock
137 */ 137 */
138struct sk_buff *br_handle_frame(struct net_bridge_port *p, struct sk_buff *skb) 138struct sk_buff *br_handle_frame(struct sk_buff *skb)
139{ 139{
140 struct net_bridge_port *p;
140 const unsigned char *dest = eth_hdr(skb)->h_dest; 141 const unsigned char *dest = eth_hdr(skb)->h_dest;
141 int (*rhook)(struct sk_buff *skb); 142 int (*rhook)(struct sk_buff *skb);
142 143
144 if (skb->pkt_type == PACKET_LOOPBACK)
145 return skb;
146
143 if (!is_valid_ether_addr(eth_hdr(skb)->h_source)) 147 if (!is_valid_ether_addr(eth_hdr(skb)->h_source))
144 goto drop; 148 goto drop;
145 149
@@ -147,6 +151,8 @@ struct sk_buff *br_handle_frame(struct net_bridge_port *p, struct sk_buff *skb)
147 if (!skb) 151 if (!skb)
148 return NULL; 152 return NULL;
149 153
154 p = br_port_get_rcu(skb->dev);
155
150 if (unlikely(is_link_local(dest))) { 156 if (unlikely(is_link_local(dest))) {
151 /* Pause frames shouldn't be passed up by driver anyway */ 157 /* Pause frames shouldn't be passed up by driver anyway */
152 if (skb->protocol == htons(ETH_P_PAUSE)) 158 if (skb->protocol == htons(ETH_P_PAUSE))