diff options
Diffstat (limited to 'drivers/net/usb/cdc_mbim.c')
-rw-r--r-- | drivers/net/usb/cdc_mbim.c | 57 |
1 files changed, 41 insertions, 16 deletions
diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c index c9f3281506af..2e025ddcef21 100644 --- a/drivers/net/usb/cdc_mbim.c +++ b/drivers/net/usb/cdc_mbim.c | |||
@@ -120,6 +120,16 @@ static void cdc_mbim_unbind(struct usbnet *dev, struct usb_interface *intf) | |||
120 | cdc_ncm_unbind(dev, intf); | 120 | cdc_ncm_unbind(dev, intf); |
121 | } | 121 | } |
122 | 122 | ||
123 | /* verify that the ethernet protocol is IPv4 or IPv6 */ | ||
124 | static bool is_ip_proto(__be16 proto) | ||
125 | { | ||
126 | switch (proto) { | ||
127 | case htons(ETH_P_IP): | ||
128 | case htons(ETH_P_IPV6): | ||
129 | return true; | ||
130 | } | ||
131 | return false; | ||
132 | } | ||
123 | 133 | ||
124 | static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) | 134 | static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) |
125 | { | 135 | { |
@@ -128,6 +138,7 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb | |||
128 | struct cdc_ncm_ctx *ctx = info->ctx; | 138 | struct cdc_ncm_ctx *ctx = info->ctx; |
129 | __le32 sign = cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN); | 139 | __le32 sign = cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN); |
130 | u16 tci = 0; | 140 | u16 tci = 0; |
141 | bool is_ip; | ||
131 | u8 *c; | 142 | u8 *c; |
132 | 143 | ||
133 | if (!ctx) | 144 | if (!ctx) |
@@ -137,25 +148,32 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb | |||
137 | if (skb->len <= ETH_HLEN) | 148 | if (skb->len <= ETH_HLEN) |
138 | goto error; | 149 | goto error; |
139 | 150 | ||
151 | /* Some applications using e.g. packet sockets will | ||
152 | * bypass the VLAN acceleration and create tagged | ||
153 | * ethernet frames directly. We primarily look for | ||
154 | * the accelerated out-of-band tag, but fall back if | ||
155 | * required | ||
156 | */ | ||
157 | skb_reset_mac_header(skb); | ||
158 | if (vlan_get_tag(skb, &tci) < 0 && skb->len > VLAN_ETH_HLEN && | ||
159 | __vlan_get_tag(skb, &tci) == 0) { | ||
160 | is_ip = is_ip_proto(vlan_eth_hdr(skb)->h_vlan_encapsulated_proto); | ||
161 | skb_pull(skb, VLAN_ETH_HLEN); | ||
162 | } else { | ||
163 | is_ip = is_ip_proto(eth_hdr(skb)->h_proto); | ||
164 | skb_pull(skb, ETH_HLEN); | ||
165 | } | ||
166 | |||
140 | /* mapping VLANs to MBIM sessions: | 167 | /* mapping VLANs to MBIM sessions: |
141 | * no tag => IPS session <0> | 168 | * no tag => IPS session <0> |
142 | * 1 - 255 => IPS session <vlanid> | 169 | * 1 - 255 => IPS session <vlanid> |
143 | * 256 - 511 => DSS session <vlanid - 256> | 170 | * 256 - 511 => DSS session <vlanid - 256> |
144 | * 512 - 4095 => unsupported, drop | 171 | * 512 - 4095 => unsupported, drop |
145 | */ | 172 | */ |
146 | vlan_get_tag(skb, &tci); | ||
147 | |||
148 | switch (tci & 0x0f00) { | 173 | switch (tci & 0x0f00) { |
149 | case 0x0000: /* VLAN ID 0 - 255 */ | 174 | case 0x0000: /* VLAN ID 0 - 255 */ |
150 | /* verify that datagram is IPv4 or IPv6 */ | 175 | if (!is_ip) |
151 | skb_reset_mac_header(skb); | ||
152 | switch (eth_hdr(skb)->h_proto) { | ||
153 | case htons(ETH_P_IP): | ||
154 | case htons(ETH_P_IPV6): | ||
155 | break; | ||
156 | default: | ||
157 | goto error; | 176 | goto error; |
158 | } | ||
159 | c = (u8 *)&sign; | 177 | c = (u8 *)&sign; |
160 | c[3] = tci; | 178 | c[3] = tci; |
161 | break; | 179 | break; |
@@ -169,7 +187,6 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb | |||
169 | "unsupported tci=0x%04x\n", tci); | 187 | "unsupported tci=0x%04x\n", tci); |
170 | goto error; | 188 | goto error; |
171 | } | 189 | } |
172 | skb_pull(skb, ETH_HLEN); | ||
173 | } | 190 | } |
174 | 191 | ||
175 | spin_lock_bh(&ctx->mtx); | 192 | spin_lock_bh(&ctx->mtx); |
@@ -204,17 +221,23 @@ static void do_neigh_solicit(struct usbnet *dev, u8 *buf, u16 tci) | |||
204 | return; | 221 | return; |
205 | 222 | ||
206 | /* need to send the NA on the VLAN dev, if any */ | 223 | /* need to send the NA on the VLAN dev, if any */ |
207 | if (tci) | 224 | rcu_read_lock(); |
225 | if (tci) { | ||
208 | netdev = __vlan_find_dev_deep(dev->net, htons(ETH_P_8021Q), | 226 | netdev = __vlan_find_dev_deep(dev->net, htons(ETH_P_8021Q), |
209 | tci); | 227 | tci); |
210 | else | 228 | if (!netdev) { |
229 | rcu_read_unlock(); | ||
230 | return; | ||
231 | } | ||
232 | } else { | ||
211 | netdev = dev->net; | 233 | netdev = dev->net; |
212 | if (!netdev) | 234 | } |
213 | return; | 235 | dev_hold(netdev); |
236 | rcu_read_unlock(); | ||
214 | 237 | ||
215 | in6_dev = in6_dev_get(netdev); | 238 | in6_dev = in6_dev_get(netdev); |
216 | if (!in6_dev) | 239 | if (!in6_dev) |
217 | return; | 240 | goto out; |
218 | is_router = !!in6_dev->cnf.forwarding; | 241 | is_router = !!in6_dev->cnf.forwarding; |
219 | in6_dev_put(in6_dev); | 242 | in6_dev_put(in6_dev); |
220 | 243 | ||
@@ -224,6 +247,8 @@ static void do_neigh_solicit(struct usbnet *dev, u8 *buf, u16 tci) | |||
224 | true /* solicited */, | 247 | true /* solicited */, |
225 | false /* override */, | 248 | false /* override */, |
226 | true /* inc_opt */); | 249 | true /* inc_opt */); |
250 | out: | ||
251 | dev_put(netdev); | ||
227 | } | 252 | } |
228 | 253 | ||
229 | static bool is_neigh_solicit(u8 *buf, size_t len) | 254 | static bool is_neigh_solicit(u8 *buf, size_t len) |