| /* |
| * Copyright (c) 2013 Mindspeed Technologies, Inc. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version 2 |
| * of the License, or (at your option) any later version. |
| |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| * |
| * |
| */ |
| |
| #ifndef _IPV6_H_ |
| #define _IPV6_H_ |
| |
| #include <types.h> |
| #include <hal.h> |
| |
| typedef struct IPv6_HDR_STRUCT |
| { |
| u16 Version_TC_FLHi; |
| u16 FlowLabelLo; |
| u16 TotalLength; |
| u8 NextHeader; |
| u8 HopLimit; |
| u32 SourceAddress[4]; |
| u32 DestinationAddress[4]; |
| } ipv6_hdr_t; |
| |
| typedef struct IPv6_FRAG_HDR |
| { |
| u8 NextHeader; |
| u8 rsvd; |
| u16 FragOffset; |
| u32 Identification; |
| } ipv6_frag_hdr_t; |
| |
| |
| typedef struct IPv6_ROUTING_HDR |
| { |
| u8 NextHeader; |
| u8 HdrExtLen; |
| u8 RoutingType; |
| u8 SegmentsLeft; |
| u8 TypeSpecificData[0]; |
| } ipv6_routing_hdr_t; |
| |
| /* IPv6 Next Header values */ |
| #define IPV6_HOP_BY_HOP 0 |
| #define IPV6_IPIP 4 |
| #define IPV6_TCP 6 |
| #define IPV6_UDP 17 |
| #define IPV6_ROUTING 43 |
| #define IPV6_FRAGMENT 44 |
| #define IPV6_ESP 50 |
| #define IPV6_AUTHENTICATE 51 |
| #define IPV6_ICMP 58 |
| #define IPV6_NONE 59 |
| #define IPV6_DESTOPT 60 |
| #define IPV6_ETHERIP 97 |
| |
| /* ICMPv6 Type Values */ |
| #define ICMPV6_ROUTER_SOLICIT 133 |
| #define ICMPV6_ROUTER_ADVT 134 |
| #define ICMPV6_NEIGH_SOLICIT 135 |
| #define ICMPV6_NEIGH_ADVT 136 |
| |
| |
| |
| #define IP6_MF 0x0001 |
| #define IP6_FRAGOFFSET 0xFFF8 |
| |
| #define IPV6_MIN_MTU 1280 |
| |
| #define IPV6_ADDRESS_LENGTH 16 |
| |
| #define IPV6_HDR_SIZE sizeof(ipv6_hdr_t) |
| |
| |
| #ifdef ENDIAN_LITTLE |
| |
| #define IPV6_GET_TRAFFIC_CLASS(phdr) ((((phdr)->Version_TC_FLHi & 0x000F) << 4) | (phdr)->Version_TC_FLHi >> 12) |
| |
| #define IPV6_SET_TRAFFIC_CLASS(phdr, tc) do { \ |
| u16 temp = (phdr)->Version_TC_FLHi & 0x0FF0; \ |
| temp |= (tc) >> 4; \ |
| temp |= ((tc) & 0xF) << 12; \ |
| (phdr)->Version_TC_FLHi = temp; \ |
| } while (0) |
| |
| #define IPV6_GET_VERSION(phdr) (((phdr)->Version_TC_FLHi >> 4) & 0xF) |
| |
| #define IPV6_SET_VERSION(phdr, vers) do { \ |
| u16 temp = (phdr)->Version_TC_FLHi & 0xFF0F; \ |
| temp |= (vers) << 4; \ |
| (phdr)->Version_TC_FLHi = temp; \ |
| } while (0) |
| |
| #define IPV6_GET_FLOW_LABEL_HI(phdr) (((phdr)->Version_TC_FLHi >> 8) & 0x000F) |
| |
| #define IPV6_SET_FLOW_LABEL_HI(phdr, flhi) do { \ |
| u16 temp = (phdr)->Version_TC_FLHi & 0xF0FF; \ |
| temp |= (flhi) << 8; \ |
| (phdr)->Version_TC_FLHi = temp; \ |
| } while (0) |
| |
| #define IPV6_SET_FLOW_LABEL(phdr, fl) do { \ |
| u16 flhi = ((fl) >> 16) & 0x000f; \ |
| IPV6_SET_FLOW_LABEL_HI((phdr), flhi); \ |
| (phdr)->FlowLabelLo = htons((fl) & 0xffff); \ |
| } while (0) |
| |
| #define IPV6_COPY_FLOW_LABEL(phdr_to, phdr_from) do { \ |
| IPV6_SET_FLOW_LABEL_HI((phdr_to), IPV6_GET_FLOW_LABEL_HI(phdr_from)); \ |
| (phdr_to)->FlowLabelLo = (phdr_from)->FlowLabelLo; \ |
| } while (0) |
| |
| #else |
| |
| #define IPV6_GET_TRAFFIC_CLASS(phdr) (((phdr)->Version_TC_FLHi >> 4) & 0xFF) |
| |
| #define IPV6_SET_TRAFFIC_CLASS(phdr, tc) do { \ |
| u16 temp = (phdr)->Version_TC_FLHi & 0xF00F; \ |
| temp |= (tc) << 4; \ |
| (phdr)->Version_TC_FLHi = temp; \ |
| } while (0) |
| |
| #define IPV6_GET_VERSION(phdr) ((phdr)->Version_TC_FLHi >> 12) |
| |
| #define IPV6_SET_VERSION(phdr, vers) do { \ |
| u16 temp = (phdr)->Version_TC_FLHi & 0x0FFF; \ |
| temp |= (vers) << 12; \ |
| (phdr)->Version_TC_FLHi = temp; \ |
| } while (0) |
| |
| #define IPV6_GET_FLOW_LABEL_HI(phdr) ((phdr)->Version_TC_FLHi & 0x000F) |
| |
| #define IPV6_SET_FLOW_LABEL_HI(phdr, flhi) do { \ |
| u16 temp = (phdr)->Version_TC_FLHi & 0xFFF0; \ |
| temp |= (flhi); \ |
| (phdr)->Version_TC_FLHi = temp; \ |
| } while (0) |
| |
| #define IPV6_SET_FLOW_LABEL(phdr, fl) do { \ |
| u16 flhi = ((fl) >> 16) & 0x000f; \ |
| IPV6_SET_FLOW_LABEL_HI((phdr), flhi); \ |
| (phdr)->FlowLabelLo = htons((fl) & 0xffff); \ |
| } while (0) |
| |
| #define IPV6_COPY_FLOW_LABEL(phdr_to, phdr_from) do { \ |
| IPV6_SET_FLOW_LABEL_HI((phdr_to), IPV6_GET_FLOW_LABEL_HI(phdr_from)); \ |
| (phdr_to)->FlowLabelLo = (phdr_from)->FlowLabelLo; \ |
| } while (0) |
| |
| #endif |
| |
| int ipv6_cmp(void *src, void *dst); |
| |
| #define IPV6_CMP(addr1, addr2) ipv6_cmp(addr1, addr2) |
| |
| |
| static inline u32 is_ipv6_addr_any(u32 *addr) |
| { |
| return ((addr[0] | addr[1] | addr[2] | addr[3]) == 0); |
| } |
| |
| |
| /** Compares 2 IPv6 addresses based on a mask. |
| * No assumptions are made on the alignment of addresses. |
| * @param a1 Pointer to first IPv6 address. |
| * @param a2 Pointer to second IPv6 address. |
| * @param masklen Length of mask to use during comparison (a length of 128 means the 2 addresses must be identical). |
| * @return 1 if the 2 addresses match, 0 otherwise. |
| */ |
| #if defined(COMCERTO_2000_CLASS) || defined(COMCERTO_2000_UTIL) |
| static inline int ipv6_cmp_mask(u32 *a1, u32 *a2, unsigned int masklen) |
| { |
| u16 *a1s = (u16 *)a1; |
| u16 *a2s = (u16 *)a2; |
| |
| while (masklen > 0) { |
| if (masklen >= 16) { |
| if (*a1s != *a2s) |
| return 0; |
| |
| masklen -= 16; |
| a1s++; |
| a2s++; |
| } else { |
| if ((ntohs(*a1s ^ *a2s) >> (16 - masklen)) != 0) |
| return 0; |
| else |
| return 1; |
| } |
| } |
| |
| return 1; |
| } |
| |
| #else |
| |
| static inline int ipv6_cmp_mask(u32 *a1, u32 *a2, unsigned int masklen) |
| { |
| |
| // when memcmp is present - can be rewritten to burst-compare words. |
| while (masklen > 0) { |
| |
| if (masklen >= 32) { |
| if (*a1 != *a2) |
| return 0; |
| |
| masklen -= 32; |
| a1++; |
| a2++; |
| } else { |
| |
| if ((ntohl(*a1 ^ *a2) >> (32 - masklen)) != 0) |
| return 0; |
| else |
| return 1; |
| } |
| } |
| |
| return 1; |
| } |
| #endif |
| |
| static inline int IS_ICMPv6(void *phdr) |
| { |
| u8 proto; |
| u8 *pNextHdr; |
| ipv6_hdr_t *ipv6_hdr = phdr; |
| |
| proto = ipv6_hdr->NextHeader; |
| pNextHdr = (u8 *) (ipv6_hdr + 1); |
| |
| // parse through any extension headers |
| while (proto != IPV6_ICMP) |
| { |
| u32 xHdrLen; |
| |
| if ((proto != IPV6_ROUTING) && (proto != IPV6_HOP_BY_HOP) && (proto != IPV6_DESTOPT)) |
| return 0; |
| |
| proto = *pNextHdr; |
| xHdrLen = (pNextHdr[1] + 1) * 8; |
| pNextHdr += xHdrLen; |
| } |
| |
| return 1; |
| } |
| |
| |
| static inline u32 ipv6_find_fragopt(ipv6_hdr_t *ip6h, u8 **nhdr, u32 pktlen) |
| { |
| // returns length of unfragmentable part of ipv6 packet and pointer to nexthdr |
| // loosely similar to ip6_find_1stfragopt |
| int rthdr_seen = 0; |
| u16 offset = sizeof(ipv6_hdr_t); |
| u8 *this_exthdr= (u8 *) (ip6h + 1); |
| |
| *nhdr = &ip6h->NextHeader; |
| |
| while (offset + 1 < pktlen) { |
| |
| switch (**nhdr) { |
| case IPV6_DESTOPT: |
| if (rthdr_seen) // if we saw routing header - all done |
| return offset; |
| |
| case IPV6_HOP_BY_HOP: |
| break; |
| |
| case IPV6_ROUTING: |
| rthdr_seen = 1; |
| break; |
| |
| default: |
| return offset; |
| } |
| |
| offset += (*(*nhdr + 1) + 1) << 3; |
| *nhdr = this_exthdr; |
| this_exthdr += (*(*nhdr + 1) + 1) << 3; |
| } |
| |
| return offset; |
| } |
| |
| static inline u8 ipv6_find_proto(ipv6_hdr_t *ip6h, u8 **nhdr, u32 pktlen) |
| { |
| // very similar to ipv6_find_firstfragopt above except returns protocol found |
| // and does not maintain offset |
| int rthdr_seen = 0; |
| u16 offset = sizeof(ipv6_hdr_t); |
| u8 *this_exthdr = (u8 *)(ip6h + 1); |
| |
| *nhdr = &ip6h->NextHeader; |
| |
| while (offset + 1 < pktlen) { |
| switch (**nhdr) { |
| case IPV6_DESTOPT: |
| if (rthdr_seen) // if we saw routing header - all done |
| return IPV6_DESTOPT; |
| |
| case IPV6_HOP_BY_HOP: |
| break; |
| |
| case IPV6_ROUTING: |
| rthdr_seen = 1; |
| break; |
| |
| default: |
| return **nhdr; |
| } |
| |
| offset += (*(*nhdr + 1) + 1) << 3; |
| *nhdr = this_exthdr; |
| this_exthdr += (*(*nhdr + 1) + 1) << 3; |
| } |
| |
| return **nhdr; |
| } |
| |
| static inline u8 *ipv6_find_frag_header(ipv6_hdr_t *ip6h, u32 pktlen) |
| { |
| // returns length of unfragmentable part of ipv6 packet and pointer to nexthdr |
| // loosely similar to ip6_find_1stfragopt |
| u8 *nhdr; |
| u16 offset = sizeof(ipv6_hdr_t); |
| u8 *this_exthdr = (u8 *)(ip6h + 1); |
| |
| nhdr = &ip6h->NextHeader; |
| |
| while (offset + 1 < pktlen) { |
| if (*nhdr == IPV6_FRAGMENT) |
| return (nhdr); |
| |
| offset += (*(nhdr + 1) + 1) << 3; |
| nhdr = this_exthdr; |
| this_exthdr += (*(nhdr + 1) + 1) << 3; |
| } |
| |
| return nhdr; |
| } |
| |
| static inline u32 ipv6_pseudoheader_checksum(u32 *saddr, u32 *daddr, u16 length, u8 nexthdr) |
| { |
| u32 sum = 0; |
| int i; |
| |
| for (i = 0; i < 8; i++) |
| sum += ((u16 *)saddr)[i] + ((u16 *)daddr)[i]; |
| |
| sum += length; |
| sum += ntohs((u16)nexthdr); |
| |
| return sum; |
| } |
| |
| #endif /* _IPV6_H_ */ |
| |