1/*
2 * Copyright (c) 2008-2016 Solarflare Communications Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 *    this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 *    this list of conditions and the following disclaimer in the documentation
12 *    and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 * The views and conclusions contained in the software and documentation are
27 * those of the authors and should not be interpreted as representing official
28 * policies, either expressed or implied, of the FreeBSD Project.
29 */
30
31#include <sys/types.h>
32#include <sys/sysmacros.h>
33#include <sys/ddi.h>
34#include <sys/sunddi.h>
35#include <sys/stream.h>
36#include <sys/strsun.h>
37#include <sys/strsubr.h>
38#include <sys/pattr.h>
39
40#include <sys/ethernet.h>
41#include <inet/ip.h>
42
43#include <netinet/in.h>
44#include <netinet/ip.h>
45#include <netinet/tcp.h>
46#include <netinet/udp.h>
47#include <netinet/sctp.h>
48
49#include "sfxge.h"
50
51#include "efx.h"
52
53
54/*
55 * Parse packet headers and return:
56 *	etherhpp	Ethernet MAC header
57 *	iphpp		IPv4 header (NULL for non-IPv4 packet)
58 *	thpp		TCP header (NULL for non-TCP packet)
59 *	offp		Offset to TCP payload
60 *	sizep		Size of TCP payload
61 *	dportp		TCP/UDP/SCTP dest. port (network order), otherwise zero
62 *	sportp		TCP/UDP/SCTP source port, (network order) otherwise zero
63 */
64sfxge_packet_type_t
65sfxge_pkthdr_parse(mblk_t *mp, struct ether_header **etherhpp,
66    struct ip **iphpp, struct tcphdr **thpp,
67    size_t *offp, size_t *sizep,
68    uint16_t *sportp, uint16_t *dportp)
69{
70	struct ether_header *etherhp;
71	uint16_t ether_type;
72	size_t etherhs;
73	struct ip *iphp;
74	size_t iphs;
75	struct tcphdr *thp;
76	size_t len;
77	size_t ths;
78	size_t off;
79	size_t size;
80	uint16_t sport;
81	uint16_t dport;
82	sfxge_packet_type_t pkt_type = SFXGE_PACKET_TYPE_UNKNOWN;
83
84	etherhp = NULL;
85	iphp = NULL;
86	thp = NULL;
87	off = 0;
88	size = 0;
89	sport = 0;
90	dport = 0;
91
92	/* Grab the MAC header */
93	etherhs = sizeof (struct ether_header);
94	if ((MBLKL(mp) < etherhs) && (pullupmsg(mp, etherhs) == 0))
95		goto done;
96
97	/*LINTED*/
98	etherhp = (struct ether_header *)(mp->b_rptr);
99	ether_type = etherhp->ether_type;
100
101	if (ether_type == htons(ETHERTYPE_VLAN)) {
102		struct ether_vlan_header *ethervhp;
103
104		etherhs = sizeof (struct ether_vlan_header);
105		if ((MBLKL(mp) < etherhs) && (pullupmsg(mp, etherhs) == 0))
106			goto done;
107
108		/*LINTED*/
109		ethervhp = (struct ether_vlan_header *)(mp->b_rptr);
110		ether_type = ethervhp->ether_type;
111	}
112
113	if (ether_type != htons(ETHERTYPE_IP))
114		goto done;
115
116	/* Skip over the MAC header */
117	off += etherhs;
118
119	/* Grab the IP header */
120	len = off + sizeof (struct ip);
121	if ((MBLKL(mp) < len) && (pullupmsg(mp, len) == 0))
122		goto done;
123
124	/*LINTED*/
125	iphp = (struct ip *)(mp->b_rptr + off);
126	iphs = iphp->ip_hl * 4;
127
128	if (iphp->ip_v != IPV4_VERSION)
129		goto done;
130
131	/* Get the size of the packet */
132	size = ntohs(iphp->ip_len);
133
134	ASSERT3U(etherhs + size, <=, msgdsize(mp));
135
136	pkt_type = SFXGE_PACKET_TYPE_IPV4_OTHER;
137
138	/* Skip over the IP header */
139	off += iphs;
140	size -= iphs;
141
142	if (iphp->ip_p == IPPROTO_TCP) {
143		/* Grab the TCP header */
144		len = off + sizeof (struct tcphdr);
145		if ((MBLKL(mp) < len) && (pullupmsg(mp, len) == 0))
146			goto done;
147
148		/*LINTED*/
149		thp = (struct tcphdr *)(mp->b_rptr + off);
150		ths = thp->th_off * 4;
151
152		dport = thp->th_dport;
153		sport = thp->th_sport;
154
155		/* Skip over the TCP header */
156		off += ths;
157		size -= ths;
158
159		pkt_type = SFXGE_PACKET_TYPE_IPV4_TCP;
160
161	} else if (iphp->ip_p == IPPROTO_UDP) {
162		struct udphdr *uhp;
163
164		/* Grab the UDP header */
165		len = off + sizeof (struct udphdr);
166		if ((MBLKL(mp) < len) && (pullupmsg(mp, len) == 0))
167			goto done;
168
169		/*LINTED*/
170		uhp = (struct udphdr *)(mp->b_rptr + off);
171		dport = uhp->uh_dport;
172		sport = uhp->uh_sport;
173
174		/* Skip over the UDP header */
175		off += sizeof (struct udphdr);
176		size -= sizeof (struct udphdr);
177
178		pkt_type = SFXGE_PACKET_TYPE_IPV4_UDP;
179
180	} else if (iphp->ip_p == IPPROTO_SCTP) {
181		struct sctp_hdr *shp;
182
183		/* Grab the SCTP header */
184		len = off + sizeof (struct sctp_hdr);
185		if ((MBLKL(mp) < len) && (pullupmsg(mp, len) == 0))
186			goto done;
187
188		/*LINTED*/
189		shp = (struct sctp_hdr *)(mp->b_rptr + off);
190		dport = shp->sh_dport;
191		sport = shp->sh_sport;
192
193		/* Skip over the SCTP header */
194		off += sizeof (struct sctp_hdr);
195		size -= sizeof (struct sctp_hdr);
196
197		pkt_type = SFXGE_PACKET_TYPE_IPV4_SCTP;
198	}
199
200	if (MBLKL(mp) < off)
201		(void) pullupmsg(mp, off);
202
203done:
204	*etherhpp = etherhp;
205	*iphpp = iphp;
206	*thpp = thp;
207	*offp = off;
208	*sizep = size;
209	*sportp = sport;
210	*dportp = dport;
211
212	return (pkt_type);
213}
214