| /* |
| * sfe_vlan.h |
| * Shortcut flow acceleration for 802.1AD/802.1Q flow |
| * |
| * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. |
| * |
| * Permission to use, copy, modify, and/or distribute this software for any |
| * purpose with or without fee is hereby granted, provided that the above |
| * copyright notice and this permission notice appear in all copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| #ifndef __SFE_VLAN_H |
| #define __SFE_VLAN_H |
| |
| #include <linux/if_vlan.h> |
| |
| /* |
| * sfe_vlan_check_and_parse_tag() |
| * |
| * case 1: QinQ frame (e.g. outer tag = 88a80032, inner tag = 81000001): |
| * When entering this function: |
| * ----+-----------------+-----|-----+-----------+-----+--------- |
| * |DMAC |SMAC |88|a8|00|32|81|00|00|01|08|00|45|00| |
| * ----+-----------------+-----A-----+-----------+-----+--------- |
| * skb->data |
| * skb->protocol = ntohs(ETH_P_8021AD) |
| * skb->vlan_proto = 0 |
| * skb->vlan_tci = 0 |
| * skb->vlan_present = 0 |
| * When exiting: |
| * ----+-----------------+-----------+-----------+-----+--------- |
| * |DMAC |SMAC |88|a8|00|32|81|00|00|01|08|00|45|00| |
| * ----+-----------------+-----------+-----------+-----A--------- |
| * skb->data |
| * skb->protocol = ntohs(ETH_P_IP) |
| * skb->vlan_proto = 0 |
| * skb->vlan_tci = 0 |
| * skb->vlan_present = 0 |
| * l2_info->vlan_hdr_cnt = 2 |
| * l2_info->vlan_hdr[0].tpid = ntohs(ETH_P_8021AD) |
| * l2_info->vlan_hdr[0].tci = 0x0032 |
| * l2_info->vlan_hdr[1].tpid = ntohs(ETH_P_8021Q) |
| * l2_info->vlan_hdr[1].tci = 0x0001 |
| * l2_info->protocol = ETH_P_IP |
| * |
| * case 2: 802.1Q frame (e.g. the tag is 81000001): |
| * When entering this function: |
| * ----+-----------------+-----|-----+-----+--------- |
| * |DMAC |SMAC |81|00|00|01|08|00|45|00| |
| * ----+-----------------+-----A-----+-----+--------- |
| * skb->data |
| * skb->protocol = ntohs(ETH_P_8021Q) |
| * skb->vlan_proto = 0 |
| * skb->vlan_tci = 0 |
| * skb->vlan_present = 0 |
| * When exiting: |
| * ----+-----------------+-----------+-----+--------- |
| * |DMAC |SMAC |81|00|00|01|08|00|45|00| |
| * ----+-----------------+-----------+-----A--------- |
| * skb->data |
| * skb->protocol = ntohs(ETH_P_IP) |
| * skb->vlan_proto = 0 |
| * skb->vlan_tci = 0 |
| * skb->vlan_present = 0 |
| * l2_info->vlan_hdr_cnt = 1 |
| * l2_info->vlan_hdr[0].tpid = ntohs(ETH_P_8021Q) |
| * l2_info->vlan_hdr[0].tci = 0x0001 |
| * l2_info->protocol = ETH_P_IP |
| * |
| * case 3: untagged frame |
| * When entering this function: |
| * ----+-----------------+-----|--------------------- |
| * |DMAC |SMAC |08|00|45|00| |
| * ----+-----------------+-----A--------------------- |
| * skb->data |
| * skb->protocol = ntohs(ETH_P_IP) |
| * skb->vlan_proto = 0 |
| * skb->vlan_tci = 0 |
| * skb->vlan_present = 0 |
| * When exiting: |
| * ----+-----------------+-----|--------------------- |
| * |DMAC |SMAC |08|00|45|00| |
| * ----+-----------------+-----A--------------------- |
| * skb->data |
| * skb->protocol = ntohs(ETH_P_IP) |
| * skb->vlan_proto = 0 |
| * skb->vlan_tci = 0 |
| * skb->vlan_present = 0 |
| * l2_info->vlan_hdr_cnt = 0 |
| * l2_info->protocol = ETH_P_IP |
| */ |
| static inline bool sfe_vlan_check_and_parse_tag(struct sk_buff *skb, struct sfe_l2_info *l2_info) |
| { |
| struct vlan_hdr *vhdr; |
| |
| while ((skb->protocol == htons(ETH_P_8021AD) || skb->protocol == htons(ETH_P_8021Q)) && |
| l2_info->vlan_hdr_cnt < SFE_MAX_VLAN_DEPTH) { |
| if (unlikely(!pskb_may_pull(skb, VLAN_HLEN))) { |
| return false; |
| } |
| vhdr = (struct vlan_hdr *)skb->data; |
| l2_info->vlan_hdr[l2_info->vlan_hdr_cnt].tpid = skb->protocol; |
| l2_info->vlan_hdr[l2_info->vlan_hdr_cnt].tci = ntohs(vhdr->h_vlan_TCI); |
| skb->protocol = vhdr->h_vlan_encapsulated_proto; |
| l2_info->vlan_hdr_cnt++; |
| /* |
| * strip VLAN header |
| */ |
| __skb_pull(skb, VLAN_HLEN); |
| skb_reset_network_header(skb); |
| } |
| |
| l2_info->protocol = htons(skb->protocol); |
| return true; |
| } |
| |
| /* |
| * sfe_vlan_undo_parse() |
| * Restore some skb fields which are modified when parsing VLAN tags. |
| */ |
| static inline void sfe_vlan_undo_parse(struct sk_buff *skb, struct sfe_l2_info *l2_info) |
| { |
| if (l2_info->vlan_hdr_cnt == 0) { |
| return; |
| } |
| |
| skb->protocol = l2_info->vlan_hdr[0].tpid; |
| __skb_push(skb, l2_info->vlan_hdr_cnt * VLAN_HLEN); |
| } |
| |
| /* |
| * sfe_vlan_validate_ingress_tag() |
| * Validate ingress packet's VLAN tag |
| */ |
| static inline bool sfe_vlan_validate_ingress_tag( |
| struct sk_buff *skb, u8 count, struct sfe_vlan_hdr *vlan_hdr, struct sfe_l2_info *l2_info) |
| { |
| u8 i; |
| |
| if (likely(!sfe_is_l2_feature_enabled())) { |
| return true; |
| } |
| |
| if (unlikely(count != l2_info->vlan_hdr_cnt)) { |
| return false; |
| } |
| |
| for (i = 0; i < count; i++) { |
| if (unlikely(vlan_hdr[i].tpid != l2_info->vlan_hdr[i].tpid)) { |
| return false; |
| } |
| |
| if (unlikely((vlan_hdr[i].tci & VLAN_VID_MASK) != |
| (l2_info->vlan_hdr[i].tci & VLAN_VID_MASK))) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /* |
| * sfe_vlan_add_tag() |
| * Add VLAN tags at skb->data. |
| * Normally, it is called just before adding 14-byte Ethernet header. |
| * |
| * This function does not update skb->mac_header so later code |
| * needs to call skb_reset_mac_header()/skb_reset_mac_len() to |
| * get correct skb->mac_header/skb->mac_len. |
| * |
| * It assumes: |
| * - skb->protocol is set |
| * - skb has enough headroom to write VLAN tags |
| * - 0 < count <= SFE_MAX_VLAN_DEPTH |
| * |
| * When entering (e.g. skb->protocol = ntohs(ETH_P_IP) or ntohs(ETH_P_PPP_SES)): |
| * -------------------------------+--------------------- |
| * |45|00|... |
| * -------------------------------A--------------------- |
| * skb->data |
| * -------------------------------v-----------------+-----+---------- |
| * |11|00|xx|xx|xx|xx|00|21|45|00|... |
| * -------------------------------+-----------------+-----+---------- |
| * |
| * When exiting (e.g. to add outer/inner tag = 88a80032/81000001): |
| * -------------+-----------+-----+--------------------- |
| * |00|32|81|00|00|01|08|00|45|00|05|d8|.... |
| * -------A-----+-----------+-----+--------------------- |
| * skb->data |
| * -------v-----+-----------+-----+-----------------+-----+---------- |
| * |00|32|81|00|00|01|88|64|11|00|xx|xx|xx|xx|00|21|45|00| |
| * -------------+-----------+-----+-----------------+-----+---------- |
| * skb->protocol = ntohs(ETH_P_8021AD) |
| */ |
| static inline void sfe_vlan_add_tag(struct sk_buff *skb, int count, struct sfe_vlan_hdr *vlan) |
| { |
| struct vlan_hdr *vhdr; |
| int i; |
| vlan += (count - 1); |
| |
| for (i = 0; i < count; i++) { |
| vhdr = (struct vlan_hdr *)skb_push(skb, VLAN_HLEN); |
| vhdr->h_vlan_TCI = htons(vlan->tci); |
| vhdr->h_vlan_encapsulated_proto = skb->protocol; |
| skb->protocol = vlan->tpid; |
| vlan--; |
| } |
| } |
| |
| #endif /* __SFE_VLAN_H */ |