s390/qeth: guard against runt packets
authorJulian Wiedmann <jwi@linux.ibm.com>
Thu, 5 Dec 2019 13:33:02 +0000 (14:33 +0100)
committerDavid S. Miller <davem@davemloft.net>
Thu, 5 Dec 2019 20:25:05 +0000 (12:25 -0800)
Depending on a packet's type, the RX path needs to access fields in the
packet headers and thus requires a minimum packet length.
Enforce this length when building the skb.

On the other hand a single runt packet is no reason to drop the whole
RX buffer. So just skip it, and continue processing on the next packet.

Fixes: 4a71df50047f ("qeth: new qeth device driver")
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/s390/net/qeth_core.h
drivers/s390/net/qeth_core_main.c
drivers/s390/net/qeth_ethtool.c

index 293dd99b7fef307ba3bdbd8ceabb8eb8f05fa3a1..7cdebd2e329f030abfc80fc1409c4a845c5cf50d 100644 (file)
@@ -480,6 +480,7 @@ struct qeth_card_stats {
 
        u64 rx_dropped_nomem;
        u64 rx_dropped_notsupp;
+       u64 rx_dropped_runt;
 
        /* rtnl_link_stats64 */
        u64 rx_packets;
index efcbe60220d13cc20ecf4206ee47bb6823df6d5c..7285484212de0cd9d84588dd024e5e606715450c 100644 (file)
@@ -5063,9 +5063,9 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
 {
        struct qdio_buffer_element *element = *__element;
        struct qdio_buffer *buffer = qethbuffer->buffer;
+       unsigned int headroom, linear_len;
        int offset = *__offset;
        bool use_rx_sg = false;
-       unsigned int headroom;
        struct sk_buff *skb;
        int skb_len = 0;
        void *data_ptr;
@@ -5082,29 +5082,41 @@ next_packet:
        *hdr = element->addr + offset;
 
        offset += sizeof(struct qeth_hdr);
+       skb = NULL;
+
        switch ((*hdr)->hdr.l2.id) {
        case QETH_HEADER_TYPE_LAYER2:
                skb_len = (*hdr)->hdr.l2.pkt_length;
+               linear_len = ETH_HLEN;
                headroom = 0;
                break;
        case QETH_HEADER_TYPE_LAYER3:
                skb_len = (*hdr)->hdr.l3.length;
                if (!IS_LAYER3(card)) {
                        QETH_CARD_STAT_INC(card, rx_dropped_notsupp);
-                       skb = NULL;
                        goto walk_packet;
                }
 
+               if ((*hdr)->hdr.l3.flags & QETH_HDR_PASSTHRU) {
+                       linear_len = ETH_HLEN;
+                       headroom = 0;
+                       break;
+               }
+
+               if ((*hdr)->hdr.l3.flags & QETH_HDR_IPV6)
+                       linear_len = sizeof(struct ipv6hdr);
+               else
+                       linear_len = sizeof(struct iphdr);
                headroom = ETH_HLEN;
                break;
        case QETH_HEADER_TYPE_OSN:
                skb_len = (*hdr)->hdr.osn.pdu_length;
                if (!IS_OSN(card)) {
                        QETH_CARD_STAT_INC(card, rx_dropped_notsupp);
-                       skb = NULL;
                        goto walk_packet;
                }
 
+               linear_len = skb_len;
                headroom = sizeof(struct qeth_hdr);
                break;
        default:
@@ -5117,8 +5129,10 @@ next_packet:
                return NULL;
        }
 
-       if (!skb_len)
-               return NULL;
+       if (skb_len < linear_len) {
+               QETH_CARD_STAT_INC(card, rx_dropped_runt);
+               goto walk_packet;
+       }
 
        use_rx_sg = (card->options.cq == QETH_CQ_ENABLED) ||
                    ((skb_len >= card->options.rx_sg_cb) &&
@@ -6268,7 +6282,8 @@ void qeth_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
                           card->stats.rx_frame_errors +
                           card->stats.rx_fifo_errors;
        stats->rx_dropped = card->stats.rx_dropped_nomem +
-                           card->stats.rx_dropped_notsupp;
+                           card->stats.rx_dropped_notsupp +
+                           card->stats.rx_dropped_runt;
        stats->multicast = card->stats.rx_multicast;
        stats->rx_length_errors = card->stats.rx_length_errors;
        stats->rx_frame_errors = card->stats.rx_frame_errors;
index f7485c6dea25b2242b2c1c36b5e51c8f9b784013..ab59bc975719dcf14df6234b11e40f14df3fa019 100644 (file)
@@ -51,6 +51,7 @@ static const struct qeth_stats card_stats[] = {
        QETH_CARD_STAT("rx0 SG page allocs", rx_sg_alloc_page),
        QETH_CARD_STAT("rx0 dropped, no memory", rx_dropped_nomem),
        QETH_CARD_STAT("rx0 dropped, bad format", rx_dropped_notsupp),
+       QETH_CARD_STAT("rx0 dropped, runt", rx_dropped_runt),
 };
 
 #define TXQ_STATS_LEN  ARRAY_SIZE(txq_stats)