123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (C) 2017 - Cambridge Greys Limited
- * Copyright (C) 2011 - 2014 Cisco Systems Inc
- */
- #include <linux/etherdevice.h>
- #include <linux/netdevice.h>
- #include <linux/skbuff.h>
- #include <linux/slab.h>
- #include <asm/byteorder.h>
- #include <uapi/linux/ip.h>
- #include <uapi/linux/virtio_net.h>
- #include <linux/virtio_net.h>
- #include <linux/virtio_byteorder.h>
- #include <linux/netdev_features.h>
- #include "vector_user.h"
- #include "vector_kern.h"
- #define GOOD_LINEAR 512
- #define GSO_ERROR "Incoming GSO frames and GRO disabled on the interface"
- struct gre_minimal_header {
- uint16_t header;
- uint16_t arptype;
- };
- struct uml_gre_data {
- uint32_t rx_key;
- uint32_t tx_key;
- uint32_t sequence;
- bool ipv6;
- bool has_sequence;
- bool pin_sequence;
- bool checksum;
- bool key;
- struct gre_minimal_header expected_header;
- uint32_t checksum_offset;
- uint32_t key_offset;
- uint32_t sequence_offset;
- };
- struct uml_l2tpv3_data {
- uint64_t rx_cookie;
- uint64_t tx_cookie;
- uint64_t rx_session;
- uint64_t tx_session;
- uint32_t counter;
- bool udp;
- bool ipv6;
- bool has_counter;
- bool pin_counter;
- bool cookie;
- bool cookie_is_64;
- uint32_t cookie_offset;
- uint32_t session_offset;
- uint32_t counter_offset;
- };
- static int l2tpv3_form_header(uint8_t *header,
- struct sk_buff *skb, struct vector_private *vp)
- {
- struct uml_l2tpv3_data *td = vp->transport_data;
- uint32_t *counter;
- if (td->udp)
- *(uint32_t *) header = cpu_to_be32(L2TPV3_DATA_PACKET);
- (*(uint32_t *) (header + td->session_offset)) = td->tx_session;
- if (td->cookie) {
- if (td->cookie_is_64)
- (*(uint64_t *)(header + td->cookie_offset)) =
- td->tx_cookie;
- else
- (*(uint32_t *)(header + td->cookie_offset)) =
- td->tx_cookie;
- }
- if (td->has_counter) {
- counter = (uint32_t *)(header + td->counter_offset);
- if (td->pin_counter) {
- *counter = 0;
- } else {
- td->counter++;
- *counter = cpu_to_be32(td->counter);
- }
- }
- return 0;
- }
- static int gre_form_header(uint8_t *header,
- struct sk_buff *skb, struct vector_private *vp)
- {
- struct uml_gre_data *td = vp->transport_data;
- uint32_t *sequence;
- *((uint32_t *) header) = *((uint32_t *) &td->expected_header);
- if (td->key)
- (*(uint32_t *) (header + td->key_offset)) = td->tx_key;
- if (td->has_sequence) {
- sequence = (uint32_t *)(header + td->sequence_offset);
- if (td->pin_sequence)
- *sequence = 0;
- else
- *sequence = cpu_to_be32(++td->sequence);
- }
- return 0;
- }
- static int raw_form_header(uint8_t *header,
- struct sk_buff *skb, struct vector_private *vp)
- {
- struct virtio_net_hdr *vheader = (struct virtio_net_hdr *) header;
- virtio_net_hdr_from_skb(
- skb,
- vheader,
- virtio_legacy_is_little_endian(),
- false,
- 0
- );
- return 0;
- }
- static int l2tpv3_verify_header(
- uint8_t *header, struct sk_buff *skb, struct vector_private *vp)
- {
- struct uml_l2tpv3_data *td = vp->transport_data;
- uint32_t *session;
- uint64_t cookie;
- if ((!td->udp) && (!td->ipv6))
- header += sizeof(struct iphdr) /* fix for ipv4 raw */;
- /* we do not do a strict check for "data" packets as per
- * the RFC spec because the pure IP spec does not have
- * that anyway.
- */
- if (td->cookie) {
- if (td->cookie_is_64)
- cookie = *(uint64_t *)(header + td->cookie_offset);
- else
- cookie = *(uint32_t *)(header + td->cookie_offset);
- if (cookie != td->rx_cookie) {
- if (net_ratelimit())
- netdev_err(vp->dev, "uml_l2tpv3: unknown cookie id");
- return -1;
- }
- }
- session = (uint32_t *) (header + td->session_offset);
- if (*session != td->rx_session) {
- if (net_ratelimit())
- netdev_err(vp->dev, "uml_l2tpv3: session mismatch");
- return -1;
- }
- return 0;
- }
- static int gre_verify_header(
- uint8_t *header, struct sk_buff *skb, struct vector_private *vp)
- {
- uint32_t key;
- struct uml_gre_data *td = vp->transport_data;
- if (!td->ipv6)
- header += sizeof(struct iphdr) /* fix for ipv4 raw */;
- if (*((uint32_t *) header) != *((uint32_t *) &td->expected_header)) {
- if (net_ratelimit())
- netdev_err(vp->dev, "header type disagreement, expecting %0x, got %0x",
- *((uint32_t *) &td->expected_header),
- *((uint32_t *) header)
- );
- return -1;
- }
- if (td->key) {
- key = (*(uint32_t *)(header + td->key_offset));
- if (key != td->rx_key) {
- if (net_ratelimit())
- netdev_err(vp->dev, "unknown key id %0x, expecting %0x",
- key, td->rx_key);
- return -1;
- }
- }
- return 0;
- }
- static int raw_verify_header(
- uint8_t *header, struct sk_buff *skb, struct vector_private *vp)
- {
- struct virtio_net_hdr *vheader = (struct virtio_net_hdr *) header;
- if ((vheader->gso_type != VIRTIO_NET_HDR_GSO_NONE) &&
- (vp->req_size != 65536)) {
- if (net_ratelimit())
- netdev_err(
- vp->dev,
- GSO_ERROR
- );
- }
- if ((vheader->flags & VIRTIO_NET_HDR_F_DATA_VALID) > 0)
- return 1;
- virtio_net_hdr_to_skb(skb, vheader, virtio_legacy_is_little_endian());
- return 0;
- }
- static bool get_uint_param(
- struct arglist *def, char *param, unsigned int *result)
- {
- char *arg = uml_vector_fetch_arg(def, param);
- if (arg != NULL) {
- if (kstrtoint(arg, 0, result) == 0)
- return true;
- }
- return false;
- }
- static bool get_ulong_param(
- struct arglist *def, char *param, unsigned long *result)
- {
- char *arg = uml_vector_fetch_arg(def, param);
- if (arg != NULL) {
- if (kstrtoul(arg, 0, result) == 0)
- return true;
- return true;
- }
- return false;
- }
- static int build_gre_transport_data(struct vector_private *vp)
- {
- struct uml_gre_data *td;
- int temp_int;
- int temp_rx;
- int temp_tx;
- vp->transport_data = kmalloc(sizeof(struct uml_gre_data), GFP_KERNEL);
- if (vp->transport_data == NULL)
- return -ENOMEM;
- td = vp->transport_data;
- td->sequence = 0;
- td->expected_header.arptype = GRE_IRB;
- td->expected_header.header = 0;
- vp->form_header = &gre_form_header;
- vp->verify_header = &gre_verify_header;
- vp->header_size = 4;
- td->key_offset = 4;
- td->sequence_offset = 4;
- td->checksum_offset = 4;
- td->ipv6 = false;
- if (get_uint_param(vp->parsed, "v6", &temp_int)) {
- if (temp_int > 0)
- td->ipv6 = true;
- }
- td->key = false;
- if (get_uint_param(vp->parsed, "rx_key", &temp_rx)) {
- if (get_uint_param(vp->parsed, "tx_key", &temp_tx)) {
- td->key = true;
- td->expected_header.header |= GRE_MODE_KEY;
- td->rx_key = cpu_to_be32(temp_rx);
- td->tx_key = cpu_to_be32(temp_tx);
- vp->header_size += 4;
- td->sequence_offset += 4;
- } else {
- return -EINVAL;
- }
- }
- td->sequence = false;
- if (get_uint_param(vp->parsed, "sequence", &temp_int)) {
- if (temp_int > 0) {
- vp->header_size += 4;
- td->has_sequence = true;
- td->expected_header.header |= GRE_MODE_SEQUENCE;
- if (get_uint_param(
- vp->parsed, "pin_sequence", &temp_int)) {
- if (temp_int > 0)
- td->pin_sequence = true;
- }
- }
- }
- vp->rx_header_size = vp->header_size;
- if (!td->ipv6)
- vp->rx_header_size += sizeof(struct iphdr);
- return 0;
- }
- static int build_l2tpv3_transport_data(struct vector_private *vp)
- {
- struct uml_l2tpv3_data *td;
- int temp_int, temp_rxs, temp_txs;
- unsigned long temp_rx;
- unsigned long temp_tx;
- vp->transport_data = kmalloc(
- sizeof(struct uml_l2tpv3_data), GFP_KERNEL);
- if (vp->transport_data == NULL)
- return -ENOMEM;
- td = vp->transport_data;
- vp->form_header = &l2tpv3_form_header;
- vp->verify_header = &l2tpv3_verify_header;
- td->counter = 0;
- vp->header_size = 4;
- td->session_offset = 0;
- td->cookie_offset = 4;
- td->counter_offset = 4;
- td->ipv6 = false;
- if (get_uint_param(vp->parsed, "v6", &temp_int)) {
- if (temp_int > 0)
- td->ipv6 = true;
- }
- if (get_uint_param(vp->parsed, "rx_session", &temp_rxs)) {
- if (get_uint_param(vp->parsed, "tx_session", &temp_txs)) {
- td->tx_session = cpu_to_be32(temp_txs);
- td->rx_session = cpu_to_be32(temp_rxs);
- } else {
- return -EINVAL;
- }
- } else {
- return -EINVAL;
- }
- td->cookie_is_64 = false;
- if (get_uint_param(vp->parsed, "cookie64", &temp_int)) {
- if (temp_int > 0)
- td->cookie_is_64 = true;
- }
- td->cookie = false;
- if (get_ulong_param(vp->parsed, "rx_cookie", &temp_rx)) {
- if (get_ulong_param(vp->parsed, "tx_cookie", &temp_tx)) {
- td->cookie = true;
- if (td->cookie_is_64) {
- td->rx_cookie = cpu_to_be64(temp_rx);
- td->tx_cookie = cpu_to_be64(temp_tx);
- vp->header_size += 8;
- td->counter_offset += 8;
- } else {
- td->rx_cookie = cpu_to_be32(temp_rx);
- td->tx_cookie = cpu_to_be32(temp_tx);
- vp->header_size += 4;
- td->counter_offset += 4;
- }
- } else {
- return -EINVAL;
- }
- }
- td->has_counter = false;
- if (get_uint_param(vp->parsed, "counter", &temp_int)) {
- if (temp_int > 0) {
- td->has_counter = true;
- vp->header_size += 4;
- if (get_uint_param(
- vp->parsed, "pin_counter", &temp_int)) {
- if (temp_int > 0)
- td->pin_counter = true;
- }
- }
- }
- if (get_uint_param(vp->parsed, "udp", &temp_int)) {
- if (temp_int > 0) {
- td->udp = true;
- vp->header_size += 4;
- td->counter_offset += 4;
- td->session_offset += 4;
- td->cookie_offset += 4;
- }
- }
- vp->rx_header_size = vp->header_size;
- if ((!td->ipv6) && (!td->udp))
- vp->rx_header_size += sizeof(struct iphdr);
- return 0;
- }
- static int build_raw_transport_data(struct vector_private *vp)
- {
- if (uml_raw_enable_vnet_headers(vp->fds->rx_fd)) {
- if (!uml_raw_enable_vnet_headers(vp->fds->tx_fd))
- return -1;
- vp->form_header = &raw_form_header;
- vp->verify_header = &raw_verify_header;
- vp->header_size = sizeof(struct virtio_net_hdr);
- vp->rx_header_size = sizeof(struct virtio_net_hdr);
- vp->dev->hw_features |= (NETIF_F_TSO | NETIF_F_GRO);
- vp->dev->features |=
- (NETIF_F_RXCSUM | NETIF_F_HW_CSUM |
- NETIF_F_TSO | NETIF_F_GRO);
- netdev_info(
- vp->dev,
- "raw: using vnet headers for tso and tx/rx checksum"
- );
- }
- return 0;
- }
- static int build_hybrid_transport_data(struct vector_private *vp)
- {
- if (uml_raw_enable_vnet_headers(vp->fds->rx_fd)) {
- vp->form_header = &raw_form_header;
- vp->verify_header = &raw_verify_header;
- vp->header_size = sizeof(struct virtio_net_hdr);
- vp->rx_header_size = sizeof(struct virtio_net_hdr);
- vp->dev->hw_features |=
- (NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GRO);
- vp->dev->features |=
- (NETIF_F_RXCSUM | NETIF_F_HW_CSUM |
- NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GRO);
- netdev_info(
- vp->dev,
- "tap/raw hybrid: using vnet headers for tso and tx/rx checksum"
- );
- } else {
- return 0; /* do not try to enable tap too if raw failed */
- }
- if (uml_tap_enable_vnet_headers(vp->fds->tx_fd))
- return 0;
- return -1;
- }
- static int build_tap_transport_data(struct vector_private *vp)
- {
- /* "Pure" tap uses the same fd for rx and tx */
- if (uml_tap_enable_vnet_headers(vp->fds->tx_fd)) {
- vp->form_header = &raw_form_header;
- vp->verify_header = &raw_verify_header;
- vp->header_size = sizeof(struct virtio_net_hdr);
- vp->rx_header_size = sizeof(struct virtio_net_hdr);
- vp->dev->hw_features |=
- (NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GRO);
- vp->dev->features |=
- (NETIF_F_RXCSUM | NETIF_F_HW_CSUM |
- NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GRO);
- netdev_info(
- vp->dev,
- "tap: using vnet headers for tso and tx/rx checksum"
- );
- return 0;
- }
- return -1;
- }
- static int build_bess_transport_data(struct vector_private *vp)
- {
- vp->form_header = NULL;
- vp->verify_header = NULL;
- vp->header_size = 0;
- vp->rx_header_size = 0;
- return 0;
- }
- int build_transport_data(struct vector_private *vp)
- {
- char *transport = uml_vector_fetch_arg(vp->parsed, "transport");
- if (strncmp(transport, TRANS_GRE, TRANS_GRE_LEN) == 0)
- return build_gre_transport_data(vp);
- if (strncmp(transport, TRANS_L2TPV3, TRANS_L2TPV3_LEN) == 0)
- return build_l2tpv3_transport_data(vp);
- if (strncmp(transport, TRANS_RAW, TRANS_RAW_LEN) == 0)
- return build_raw_transport_data(vp);
- if (strncmp(transport, TRANS_TAP, TRANS_TAP_LEN) == 0)
- return build_tap_transport_data(vp);
- if (strncmp(transport, TRANS_HYBRID, TRANS_HYBRID_LEN) == 0)
- return build_hybrid_transport_data(vp);
- if (strncmp(transport, TRANS_BESS, TRANS_BESS_LEN) == 0)
- return build_bess_transport_data(vp);
- return 0;
- }
|