blob: 2770caff2d1fb66b9feace4385261908bd2f3e4c [file] [log] [blame]
/*
* Fundamental constants relating to IP Protocol
*
* Copyright (C) 2020, Broadcom.
*
* Unless you and Broadcom execute a separate written software license
* agreement governing use of this software, this software is licensed to you
* under the terms of the GNU General Public License version 2 (the "GPL"),
* available at http://www.broadcom.com/licenses/GPLv2.php, with the
* following added to such license:
*
* As a special exception, the copyright holders of this software give you
* permission to link this software with independent modules, and to copy and
* distribute the resulting executable under terms of your choice, provided that
* you also meet, for each linked independent module, the terms and conditions of
* the license of that module. An independent module is a module which is not
* derived from this software. The special exception does not apply to any
* modifications of the software.
*
*
* <<Broadcom-WL-IPTag/Dual:>>
*/
#ifndef _bcmproto_h_
#define _bcmproto_h_
#ifndef _TYPEDEFS_H_
#include <typedefs.h>
#endif
#include "eapol.h"
#include "802.3.h"
#include "vlan.h"
#include "bcmtcp.h"
/* copy from igsc.h */
#define IGMP_HLEN 8
enum frame_l2_hdr {
FRAME_L2_SNAP_H = 1,
FRAME_L2_SNAPVLAN_H,
FRAME_L2_ETH_H,
FRAME_L2_ETHVLAN_H,
FRAME_L2_ERROR,
};
enum frame_l3_hdr {
FRAME_L3_IP_H = 4,
FRAME_L3_IP6_H = 6,
FRAME_L3_ARP_H,
FRAME_L3_8021X_EAPOLKEY_H,
FRAME_L3_ERROR,
};
enum frame_l4_hdr {
FRAME_L4_ICMP_H = 1,
FRAME_L4_IGMP_H = 2,
FRAME_L4_TCP_H = 6,
FRAME_L4_UDP_H = 17,
FRAME_L4_ICMP6_H = 58,
FRAME_L4_ERROR,
};
typedef struct {
uint8 *l2;
uint8 l2_t;
uint16 l2_len;
uint8 *l3;
uint8 l3_t;
uint16 l3_len;
uint8 *l4;
uint8 l4_t;
uint16 l4_len;
} frame_proto_t;
static const uint8 llc_snap_hdr[SNAP_HDR_LEN] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
/* Generic header parser function */
static INLINE int
hnd_frame_proto(uint8 *p, int plen, frame_proto_t *fp)
{
struct dot3_mac_llc_snap_header *sh = (struct dot3_mac_llc_snap_header *)p;
struct dot3_mac_llc_snapvlan_header *svh = (struct dot3_mac_llc_snapvlan_header *)p;
struct ether_header *eh = (struct ether_header *)p;
struct ethervlan_header *evh = (struct ethervlan_header *)p;
uint16 type;
uint16 len;
if (p == NULL || plen <= 0) {
return BCME_ERROR;
}
if (plen < (int)sizeof(*eh)) {
return BCME_BUFTOOSHORT;
}
type = ntoh16(eh->ether_type);
bzero(fp, sizeof(frame_proto_t));
/* L2 header/pointer check */
fp->l2 = p;
fp->l2_len = (uint16)plen;
if (type < ETHER_TYPE_MIN) {
if (plen < (int)sizeof(*sh)) {
return BCME_BUFTOOSHORT;
}
if (bcmp(&sh->dsap, llc_snap_hdr, SNAP_HDR_LEN) == 0) {
type = ntoh16(sh->type);
if (type == ETHER_TYPE_8021Q) {
fp->l2_t = FRAME_L2_SNAPVLAN_H;
p += sizeof(struct dot3_mac_llc_snap_header);
if ((plen -= sizeof(struct dot3_mac_llc_snap_header)) <= 0) {
return BCME_ERROR;
}
}
else {
fp->l2_t = FRAME_L2_SNAP_H;
type = ntoh16(svh->ether_type);
p += sizeof(struct dot3_mac_llc_snapvlan_header);
if ((plen -= sizeof(struct dot3_mac_llc_snapvlan_header)) <= 0) {
return BCME_ERROR;
}
}
}
else {
return BCME_ERROR;
}
}
else {
if (type == ETHER_TYPE_8021Q) {
fp->l2_t = FRAME_L2_ETHVLAN_H;
type = ntoh16(evh->ether_type);
p += ETHERVLAN_HDR_LEN;
if ((plen -= ETHERVLAN_HDR_LEN) <= 0) {
return BCME_ERROR;
}
}
else {
fp->l2_t = FRAME_L2_ETH_H;
p += ETHER_HDR_LEN;
if ((plen -= ETHER_HDR_LEN) <= 0) {
return BCME_ERROR;
}
}
}
/* L3 header/pointer check */
fp->l3 = p;
fp->l3_len = (uint16)plen;
switch (type) {
case ETHER_TYPE_ARP: {
if ((plen -= ARP_DATA_LEN) < 0) {
return BCME_ERROR;
}
fp->l3_t = FRAME_L3_ARP_H;
/* no layer 4 protocol, return */
return BCME_OK;
break;
}
case ETHER_TYPE_IP: {
struct ipv4_hdr *iph = (struct ipv4_hdr *)p;
len = IPV4_HLEN(iph);
if ((plen -= len) <= 0) {
return BCME_ERROR;
}
if (IP_VER(iph) == IP_VER_4 && len >= IPV4_MIN_HEADER_LEN) {
fp->l3_t = FRAME_L3_IP_H;
type = IPV4_PROT(iph);
p += len;
}
else {
/* not a valid ipv4 packet */
return BCME_ERROR;
}
break;
}
case ETHER_TYPE_IPV6: {
struct ipv6_hdr *ip6h = (struct ipv6_hdr *)p;
if ((plen -= IPV6_MIN_HLEN) <= 0) {
return BCME_ERROR;
}
if (IP_VER(ip6h) == IP_VER_6) {
fp->l3_t = FRAME_L3_IP6_H;
type = IPV6_PROT(ip6h);
p += IPV6_MIN_HLEN;
if (IPV6_EXTHDR(type)) {
uint8 proto = 0;
int32 exth_len = ipv6_exthdr_len_check(p, plen, &proto);
if (exth_len < 0 || ((plen -= exth_len) <= 0))
return BCME_ERROR;
type = proto;
p += exth_len;
}
}
else {
/* not a valid ipv6 packet */
return BCME_ERROR;
}
break;
}
case ETHER_TYPE_802_1X: {
eapol_hdr_t *eapolh = (eapol_hdr_t *)p;
if ((plen -= EAPOL_HDR_LEN) <= 0) {
return BCME_ERROR;
}
if (eapolh->type == EAPOL_KEY) {
fp->l3_t = FRAME_L3_8021X_EAPOLKEY_H;
return BCME_OK;
}
else {
/* not a valid ipv6 packet */
return BCME_ERROR;
}
break;
}
default:
/* not interesting case */
return BCME_ERROR;
break;
}
/* L4 header/pointer check */
fp->l4 = p;
fp->l4_len = (uint16)plen;
switch (type) {
case IP_PROT_ICMP:
fp->l4_t = FRAME_L4_ICMP_H;
if ((plen -= sizeof(struct bcmicmp_hdr)) < 0) {
return BCME_ERROR;
}
break;
case IP_PROT_IGMP:
fp->l4_t = FRAME_L4_IGMP_H;
if ((plen -= IGMP_HLEN) < 0) {
return BCME_ERROR;
}
break;
case IP_PROT_TCP:
fp->l4_t = FRAME_L4_TCP_H;
if ((plen -= sizeof(struct bcmtcp_hdr)) < 0) {
return BCME_ERROR;
}
break;
case IP_PROT_UDP:
fp->l4_t = FRAME_L4_UDP_H;
if ((plen -= sizeof(struct bcmudp_hdr)) < 0) {
return BCME_ERROR;
}
break;
case IP_PROT_ICMP6:
fp->l4_t = FRAME_L4_ICMP6_H;
if ((plen -= sizeof(struct icmp6_hdr)) < 0) {
return BCME_ERROR;
}
break;
default:
break;
}
return BCME_OK;
}
#define SNAP_HDR_LEN 6 /* 802.3 LLC/SNAP header length */
#define FRAME_DROP 0
#define FRAME_NOP 1
#define FRAME_TAKEN 2
#endif /* _bcmproto_h_ */