1 /* Copyright (C) 2007-2013 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author Victor Julien <victor@inliniac.net>
22  * \author Brian Rectanus <brectanu@gmail.com>
23  */
24 
25 #ifndef __DECODE_IPV4_H__
26 #define __DECODE_IPV4_H__
27 
28 #define IPV4_HEADER_LEN           20    /**< Header length */
29 #define IPV4_OPTMAX               40    /**< Max options length */
30 #define	IPV4_MAXPACKET_LEN        65535 /**< Maximum packet size */
31 
32 /** IP Option Types */
33 #define IPV4_OPT_EOL              0x00  /**< Option: End of List */
34 #define IPV4_OPT_NOP              0x01  /**< Option: No op */
35 #define IPV4_OPT_RR               0x07  /**< Option: Record Route */
36 #define IPV4_OPT_QS               0x19  /**< Option: Quick Start */
37 #define IPV4_OPT_TS               0x44  /**< Option: Timestamp */
38 #define IPV4_OPT_SEC              0x82  /**< Option: Security */
39 #define IPV4_OPT_LSRR             0x83  /**< Option: Loose Source Route */
40 #define IPV4_OPT_CIPSO            0x86  /**< Option: Commercial IP Security */
41 #define IPV4_OPT_SID              0x88  /**< Option: Stream Identifier */
42 #define IPV4_OPT_SSRR             0x89  /**< Option: Strict Source Route */
43 #define IPV4_OPT_RTRALT           0x94  /**< Option: Router Alert */
44 
45 /** IP Option Lengths (fixed) */
46 #define IPV4_OPT_SEC_LEN          11    /**< SEC Option Fixed Length */
47 #define IPV4_OPT_SID_LEN          4     /**< SID Option Fixed Length */
48 #define IPV4_OPT_RTRALT_LEN       4     /**< RTRALT Option Fixed Length */
49 
50 /** IP Option Lengths (variable) */
51 #define IPV4_OPT_ROUTE_MIN        3     /**< RR, SRR, LTRR Option Min Length */
52 #define IPV4_OPT_QS_MIN           8     /**< QS Option Min Length */
53 #define IPV4_OPT_TS_MIN           5     /**< TS Option Min Length */
54 #define IPV4_OPT_CIPSO_MIN        10    /**< CIPSO Option Min Length */
55 
56 /** IP Option fields */
57 #define IPV4_OPTS                 ip4vars.ip_opts
58 #define IPV4_OPTS_CNT             ip4vars.ip_opt_cnt
59 
60 typedef struct IPV4Opt_ {
61     /** \todo We may want to break type up into its 3 fields
62      *        as the reassembler may want to know which options
63      *        must be copied to each fragment.
64      */
65     uint8_t type;         /**< option type */
66     uint8_t len;          /**< option length (type+len+data) */
67     const uint8_t *data;  /**< option data */
68 } IPV4Opt;
69 
70 typedef struct IPV4Hdr_
71 {
72     uint8_t ip_verhl;     /**< version & header length */
73     uint8_t ip_tos;       /**< type of service */
74     uint16_t ip_len;      /**< length */
75     uint16_t ip_id;       /**< id */
76     uint16_t ip_off;      /**< frag offset */
77     uint8_t ip_ttl;       /**< time to live */
78     uint8_t ip_proto;     /**< protocol (tcp, udp, etc) */
79     uint16_t ip_csum;     /**< checksum */
80     union {
81         struct {
82             struct in_addr ip_src;/**< source address */
83             struct in_addr ip_dst;/**< destination address */
84         } ip4_un1;
85         uint16_t ip_addrs[4];
86     } ip4_hdrun1;
87 } IPV4Hdr;
88 
89 
90 #define s_ip_src                          ip4_hdrun1.ip4_un1.ip_src
91 #define s_ip_dst                          ip4_hdrun1.ip4_un1.ip_dst
92 #define s_ip_addrs                        ip4_hdrun1.ip_addrs
93 
94 #define IPV4_GET_RAW_VER(ip4h)            (((ip4h)->ip_verhl & 0xf0) >> 4)
95 #define IPV4_GET_RAW_HLEN(ip4h)           ((ip4h)->ip_verhl & 0x0f)
96 #define IPV4_GET_RAW_IPTOS(ip4h)          ((ip4h)->ip_tos)
97 #define IPV4_GET_RAW_IPLEN(ip4h)          ((ip4h)->ip_len)
98 #define IPV4_GET_RAW_IPID(ip4h)           ((ip4h)->ip_id)
99 #define IPV4_GET_RAW_IPOFFSET(ip4h)       ((ip4h)->ip_off)
100 #define IPV4_GET_RAW_IPTTL(ip4h)          ((ip4h)->ip_ttl)
101 #define IPV4_GET_RAW_IPPROTO(ip4h)        ((ip4h)->ip_proto)
102 #define IPV4_GET_RAW_IPSRC(ip4h)          ((ip4h)->s_ip_src)
103 #define IPV4_GET_RAW_IPDST(ip4h)          ((ip4h)->s_ip_dst)
104 
105 /** return the raw (directly from the header) src ip as uint32_t */
106 #define IPV4_GET_RAW_IPSRC_U32(ip4h)      (uint32_t)((ip4h)->s_ip_src.s_addr)
107 /** return the raw (directly from the header) dst ip as uint32_t */
108 #define IPV4_GET_RAW_IPDST_U32(ip4h)      (uint32_t)((ip4h)->s_ip_dst.s_addr)
109 
110 /* we need to change them as well as get them */
111 #define IPV4_SET_RAW_VER(ip4h, value)     ((ip4h)->ip_verhl = (((ip4h)->ip_verhl & 0x0f) | (value << 4)))
112 #define IPV4_SET_RAW_HLEN(ip4h, value)    ((ip4h)->ip_verhl = (((ip4h)->ip_verhl & 0xf0) | (value & 0x0f)))
113 #define IPV4_SET_RAW_IPTOS(ip4h, value)   ((ip4h)->ip_tos = value)
114 #define IPV4_SET_RAW_IPLEN(ip4h, value)   ((ip4h)->ip_len = value)
115 #define IPV4_SET_RAW_IPPROTO(ip4h, value) ((ip4h)->ip_proto = value)
116 
117 /* ONLY call these functions after making sure that:
118  * 1. p->ip4h is set
119  * 2. p->ip4h is valid (len is correct)
120  */
121 #define IPV4_GET_VER(p) \
122     IPV4_GET_RAW_VER((p)->ip4h)
123 #define IPV4_GET_HLEN(p) \
124     (IPV4_GET_RAW_HLEN((p)->ip4h) << 2)
125 #define IPV4_GET_IPTOS(p) \
126     IPV4_GET_RAW_IPTOS((p)->ip4h)
127 #define IPV4_GET_IPLEN(p) \
128     (SCNtohs(IPV4_GET_RAW_IPLEN((p)->ip4h)))
129 #define IPV4_GET_IPID(p) \
130     (SCNtohs(IPV4_GET_RAW_IPID((p)->ip4h)))
131 /* _IPV4_GET_IPOFFSET: get the content of the offset header field in host order */
132 #define _IPV4_GET_IPOFFSET(p) \
133     (SCNtohs(IPV4_GET_RAW_IPOFFSET((p)->ip4h)))
134 /* IPV4_GET_IPOFFSET: get the final offset */
135 #define IPV4_GET_IPOFFSET(p) \
136     (_IPV4_GET_IPOFFSET(p) & 0x1fff)
137 /* IPV4_GET_RF: get the RF flag. Use _IPV4_GET_IPOFFSET to save a SCNtohs call. */
138 #define IPV4_GET_RF(p) \
139     (uint8_t)((_IPV4_GET_IPOFFSET((p)) & 0x8000) >> 15)
140 /* IPV4_GET_DF: get the DF flag. Use _IPV4_GET_IPOFFSET to save a SCNtohs call. */
141 #define IPV4_GET_DF(p) \
142     (uint8_t)((_IPV4_GET_IPOFFSET((p)) & 0x4000) >> 14)
143 /* IPV4_GET_MF: get the MF flag. Use _IPV4_GET_IPOFFSET to save a SCNtohs call. */
144 #define IPV4_GET_MF(p) \
145     (uint8_t)((_IPV4_GET_IPOFFSET((p)) & 0x2000) >> 13)
146 #define IPV4_GET_IPTTL(p) \
147      IPV4_GET_RAW_IPTTL(p->ip4h)
148 #define IPV4_GET_IPPROTO(p) \
149     IPV4_GET_RAW_IPPROTO((p)->ip4h)
150 
151 #define CLEAR_IPV4_PACKET(p) do { \
152     (p)->ip4h = NULL; \
153     (p)->level3_comp_csum = -1; \
154     memset(&p->ip4vars, 0x00, sizeof(p->ip4vars)); \
155 } while (0)
156 
157 enum IPV4OptionFlags {
158     IPV4_OPT_FLAG_EOL = 0,
159     IPV4_OPT_FLAG_NOP,
160     IPV4_OPT_FLAG_RR,
161     IPV4_OPT_FLAG_TS,
162     IPV4_OPT_FLAG_QS,
163     IPV4_OPT_FLAG_LSRR,
164     IPV4_OPT_FLAG_SSRR,
165     IPV4_OPT_FLAG_SID,
166     IPV4_OPT_FLAG_SEC,
167     IPV4_OPT_FLAG_CIPSO,
168     IPV4_OPT_FLAG_RTRALT,
169 };
170 
171 /* helper structure with parsed ipv4 info */
172 typedef struct IPV4Vars_
173 {
174     int32_t comp_csum;     /* checksum computed over the ipv4 packet */
175 
176     uint16_t opt_cnt;
177     uint16_t opts_set;
178 } IPV4Vars;
179 
180 
181 void DecodeIPV4RegisterTests(void);
182 
183 /** ----- Inline functions ----- */
184 static inline uint16_t IPV4Checksum(uint16_t *, uint16_t, uint16_t);
185 
186 /**
187  * \brief Calculateor validate the checksum for the IP packet
188  *
189  * \param pkt  Pointer to the start of the IP packet
190  * \param hlen Length of the IP header
191  * \param init The current checksum if validating, 0 if generating.
192  *
193  * \retval csum For validation 0 will be returned for success, for calculation
194  *    this will be the checksum.
195  */
IPV4Checksum(uint16_t * pkt,uint16_t hlen,uint16_t init)196 static inline uint16_t IPV4Checksum(uint16_t *pkt, uint16_t hlen, uint16_t init)
197 {
198     uint32_t csum = init;
199 
200     csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[6] + pkt[7] +
201         pkt[8] + pkt[9];
202 
203     hlen -= 20;
204     pkt += 10;
205 
206     if (hlen == 0) {
207         ;
208     } else if (hlen == 4) {
209         csum += pkt[0] + pkt[1];
210     } else if (hlen == 8) {
211         csum += pkt[0] + pkt[1] + pkt[2] + pkt[3];
212     } else if (hlen == 12) {
213         csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5];
214     } else if (hlen == 16) {
215         csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
216             pkt[7];
217     } else if (hlen == 20) {
218         csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
219             pkt[7] + pkt[8] + pkt[9];
220     } else if (hlen == 24) {
221         csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
222             pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11];
223     } else if (hlen == 28) {
224         csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
225             pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11] + pkt[12] + pkt[13];
226     } else if (hlen == 32) {
227         csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
228             pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11] + pkt[12] + pkt[13] +
229             pkt[14] + pkt[15];
230     } else if (hlen == 36) {
231         csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
232             pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11] + pkt[12] + pkt[13] +
233             pkt[14] + pkt[15] + pkt[16] + pkt[17];
234     } else if (hlen == 40) {
235         csum += pkt[0] + pkt[1] + pkt[2] + pkt[3] + pkt[4] + pkt[5] + pkt[6] +
236             pkt[7] + pkt[8] + pkt[9] + pkt[10] + pkt[11] + pkt[12] + pkt[13] +
237             pkt[14] + pkt[15] + pkt[16] + pkt[17] + pkt[18] + pkt[19];
238     }
239 
240     csum = (csum >> 16) + (csum & 0x0000FFFF);
241     csum += (csum >> 16);
242 
243     return (uint16_t) ~csum;
244 }
245 
246 #endif /* __DECODE_IPV4_H__ */
247 
248