|
@@ -276,14 +276,44 @@ static void macvtap_del_queues(struct net_device *dev)
|
|
|
*/
|
|
|
static int macvtap_forward(struct net_device *dev, struct sk_buff *skb)
|
|
|
{
|
|
|
+ struct macvlan_dev *vlan = netdev_priv(dev);
|
|
|
struct macvtap_queue *q = macvtap_get_queue(dev, skb);
|
|
|
+ netdev_features_t features;
|
|
|
if (!q)
|
|
|
goto drop;
|
|
|
|
|
|
if (skb_queue_len(&q->sk.sk_receive_queue) >= dev->tx_queue_len)
|
|
|
goto drop;
|
|
|
|
|
|
- skb_queue_tail(&q->sk.sk_receive_queue, skb);
|
|
|
+ skb->dev = dev;
|
|
|
+ /* Apply the forward feature mask so that we perform segmentation
|
|
|
+ * according to users wishes.
|
|
|
+ */
|
|
|
+ features = netif_skb_features(skb) & vlan->tap_features;
|
|
|
+ if (netif_needs_gso(skb, features)) {
|
|
|
+ struct sk_buff *segs = __skb_gso_segment(skb, features, false);
|
|
|
+
|
|
|
+ if (IS_ERR(segs))
|
|
|
+ goto drop;
|
|
|
+
|
|
|
+ if (!segs) {
|
|
|
+ skb_queue_tail(&q->sk.sk_receive_queue, skb);
|
|
|
+ goto wake_up;
|
|
|
+ }
|
|
|
+
|
|
|
+ kfree_skb(skb);
|
|
|
+ while (segs) {
|
|
|
+ struct sk_buff *nskb = segs->next;
|
|
|
+
|
|
|
+ segs->next = NULL;
|
|
|
+ skb_queue_tail(&q->sk.sk_receive_queue, segs);
|
|
|
+ segs = nskb;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ skb_queue_tail(&q->sk.sk_receive_queue, skb);
|
|
|
+ }
|
|
|
+
|
|
|
+wake_up:
|
|
|
wake_up_interruptible_poll(sk_sleep(&q->sk), POLLIN | POLLRDNORM | POLLRDBAND);
|
|
|
return NET_RX_SUCCESS;
|
|
|
|