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) |
