]>
Commit | Line | Data |
---|---|---|
83067e5a | 1 | /* Copyright (C) 2012-2021 Open Information Security Foundation |
6480cd1b EL |
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 | * \ingroup decode | |
20 | * | |
21 | * @{ | |
22 | */ | |
23 | ||
24 | ||
25 | /** | |
26 | * \file | |
27 | * | |
28 | * \author Eric Leblond <eric@regit.org> | |
29 | * | |
2732faf0 EL |
30 | * Decode Teredo Tunneling protocol. |
31 | * | |
32 | * This implementation is based upon RFC 4380: http://www.ietf.org/rfc/rfc4380.txt | |
6480cd1b EL |
33 | */ |
34 | ||
35 | #include "suricata-common.h" | |
36 | #include "decode.h" | |
37 | #include "decode-ipv6.h" | |
ab1200fb | 38 | #include "decode-teredo.h" |
83067e5a JL |
39 | |
40 | #include "util-validate.h" | |
6480cd1b | 41 | #include "util-debug.h" |
62b6f9fe | 42 | #include "conf.h" |
e97cdb48 | 43 | #include "detect-engine-port.h" |
6480cd1b | 44 | |
e97cdb48 | 45 | #define TEREDO_ORIG_INDICATION_LENGTH 8 |
6be08637 AJK |
46 | #define TEREDO_MAX_PORTS 4 |
47 | #define TEREDO_UNSET_PORT -1 | |
2732faf0 | 48 | |
62b6f9fe | 49 | static bool g_teredo_enabled = true; |
e97cdb48 | 50 | static bool g_teredo_ports_any = true; |
6be08637 AJK |
51 | static int g_teredo_ports_cnt = 0; |
52 | static int g_teredo_ports[TEREDO_MAX_PORTS] = { TEREDO_UNSET_PORT, TEREDO_UNSET_PORT, | |
53 | TEREDO_UNSET_PORT, TEREDO_UNSET_PORT }; | |
e97cdb48 VJ |
54 | |
55 | bool DecodeTeredoEnabledForPort(const uint16_t sp, const uint16_t dp) | |
56 | { | |
6be08637 | 57 | SCLogDebug("ports %u->%u ports %d %d %d %d", sp, dp, g_teredo_ports[0], g_teredo_ports[1], |
e97cdb48 VJ |
58 | g_teredo_ports[2], g_teredo_ports[3]); |
59 | ||
60 | if (g_teredo_enabled) { | |
61 | /* no port config means we are enabled for all ports */ | |
62 | if (g_teredo_ports_any) { | |
63 | return true; | |
64 | } | |
65 | ||
66 | for (int i = 0; i < g_teredo_ports_cnt; i++) { | |
6be08637 | 67 | if (g_teredo_ports[i] == TEREDO_UNSET_PORT) |
e97cdb48 VJ |
68 | return false; |
69 | const int port = g_teredo_ports[i]; | |
6be08637 | 70 | if (port == (const int)sp || port == (const int)dp) |
e97cdb48 VJ |
71 | return true; |
72 | } | |
73 | } | |
74 | return false; | |
75 | } | |
76 | ||
77 | static void DecodeTeredoConfigPorts(const char *pstr) | |
78 | { | |
79 | SCLogDebug("parsing \'%s\'", pstr); | |
80 | ||
81 | if (strcmp(pstr, "any") == 0) { | |
82 | g_teredo_ports_any = true; | |
83 | return; | |
84 | } | |
85 | ||
86 | DetectPort *head = NULL; | |
87 | DetectPortParse(NULL, &head, pstr); | |
88 | ||
89 | g_teredo_ports_any = false; | |
90 | g_teredo_ports_cnt = 0; | |
91 | for (DetectPort *p = head; p != NULL; p = p->next) { | |
6be08637 AJK |
92 | if (g_teredo_ports_cnt >= TEREDO_MAX_PORTS) { |
93 | SCLogWarning(SC_ERR_INVALID_YAML_CONF_ENTRY, "only %d Teredo ports can be defined", | |
94 | TEREDO_MAX_PORTS); | |
e97cdb48 VJ |
95 | break; |
96 | } | |
97 | g_teredo_ports[g_teredo_ports_cnt++] = (int)p->port; | |
98 | } | |
6be08637 | 99 | |
e97cdb48 VJ |
100 | DetectPortCleanupList(NULL, head); |
101 | } | |
62b6f9fe VJ |
102 | |
103 | void DecodeTeredoConfig(void) | |
104 | { | |
105 | int enabled = 0; | |
106 | if (ConfGetBool("decoder.teredo.enabled", &enabled) == 1) { | |
107 | if (enabled) { | |
108 | g_teredo_enabled = true; | |
109 | } else { | |
110 | g_teredo_enabled = false; | |
111 | } | |
112 | } | |
e97cdb48 VJ |
113 | if (g_teredo_enabled) { |
114 | ConfNode *node = ConfGetNode("decoder.teredo.ports"); | |
115 | if (node && node->val) { | |
116 | DecodeTeredoConfigPorts(node->val); | |
117 | } | |
118 | } | |
62b6f9fe VJ |
119 | } |
120 | ||
6480cd1b EL |
121 | /** |
122 | * \brief Function to decode Teredo packets | |
123 | * | |
d4b7ecfb | 124 | * \retval TM_ECODE_FAILED if packet is not a Teredo packet, TM_ECODE_OK if it is |
6480cd1b | 125 | */ |
579cc9f0 | 126 | int DecodeTeredo(ThreadVars *tv, DecodeThreadVars *dtv, Packet *p, |
f8aed4ce | 127 | const uint8_t *pkt, uint16_t len) |
6480cd1b | 128 | { |
83067e5a JL |
129 | DEBUG_VALIDATE_BUG_ON(pkt == NULL); |
130 | ||
62b6f9fe VJ |
131 | if (!g_teredo_enabled) |
132 | return TM_ECODE_FAILED; | |
6480cd1b | 133 | |
579cc9f0 | 134 | const uint8_t *start = pkt; |
6480cd1b EL |
135 | |
136 | /* Is this packet to short to contain an IPv6 packet ? */ | |
137 | if (len < IPV6_HEADER_LEN) | |
d4b7ecfb | 138 | return TM_ECODE_FAILED; |
6480cd1b EL |
139 | |
140 | /* Teredo encapsulate IPv6 in UDP and can add some custom message | |
2732faf0 EL |
141 | * part before the IPv6 packet. In our case, we just want to get |
142 | * over an ORIGIN indication. So we just make one offset if needed. */ | |
143 | if (start[0] == 0x0) { | |
6be08637 AJK |
144 | /* origin indication: compatible with tunnel */ |
145 | if (start[1] == 0x0) { | |
146 | /* offset is not coherent with len and presence of an IPv6 header */ | |
147 | if (len < TEREDO_ORIG_INDICATION_LENGTH + IPV6_HEADER_LEN) | |
d4b7ecfb | 148 | return TM_ECODE_FAILED; |
6be08637 AJK |
149 | |
150 | start += TEREDO_ORIG_INDICATION_LENGTH; | |
151 | ||
152 | /* either authentication negotiation not real tunnel or invalid second byte */ | |
153 | } else { | |
154 | return TM_ECODE_FAILED; | |
6480cd1b EL |
155 | } |
156 | } | |
157 | ||
158 | /* There is no specific field that we can check to prove that the packet | |
159 | * is a Teredo packet. We've zapped here all the possible Teredo header | |
160 | * and we should have an IPv6 packet at the start pointer. | |
11f3659f | 161 | * We then can only do a few checks before sending the encapsulated packets |
6480cd1b EL |
162 | * to decoding: |
163 | * - The packet has a protocol version which is IPv6. | |
164 | * - The IPv6 length of the packet matches what remains in buffer. | |
11f3659f VJ |
165 | * - HLIM is 0. This would technically be valid, but still weird. |
166 | * - NH 0 (HOP) and not enough data. | |
167 | * | |
168 | * If all these conditions are met, the tunnel decoder will be called. | |
169 | * If the packet gets an invalid event set, it will still be rejected. | |
6480cd1b EL |
170 | */ |
171 | if (IP_GET_RAW_VER(start) == 6) { | |
172 | IPV6Hdr *thdr = (IPV6Hdr *)start; | |
11f3659f VJ |
173 | |
174 | /* ignore hoplimit 0 packets, most likely an artifact of bad detection */ | |
175 | if (IPV6_GET_RAW_HLIM(thdr) == 0) | |
176 | return TM_ECODE_FAILED; | |
177 | ||
178 | /* if nh is 0 (HOP) with little data we have a bogus packet */ | |
179 | if (IPV6_GET_RAW_NH(thdr) == 0 && IPV6_GET_RAW_PLEN(thdr) < 8) | |
180 | return TM_ECODE_FAILED; | |
181 | ||
6480cd1b EL |
182 | if (len == IPV6_HEADER_LEN + |
183 | IPV6_GET_RAW_PLEN(thdr) + (start - pkt)) { | |
f8aed4ce VJ |
184 | int blen = len - (start - pkt); |
185 | /* spawn off tunnel packet */ | |
186 | Packet *tp = PacketTunnelPktSetup(tv, dtv, p, start, blen, | |
187 | DECODE_TUNNEL_IPV6_TEREDO); | |
188 | if (tp != NULL) { | |
189 | PKT_SET_SRC(tp, PKT_SRC_DECODER_TEREDO); | |
190 | /* add the tp to the packet queue. */ | |
191 | PacketEnqueueNoLock(&tv->decode_pq,tp); | |
192 | StatsIncr(tv, dtv->counter_teredo); | |
193 | return TM_ECODE_OK; | |
6480cd1b EL |
194 | } |
195 | } | |
d4b7ecfb | 196 | return TM_ECODE_FAILED; |
6480cd1b EL |
197 | } |
198 | ||
d4b7ecfb | 199 | return TM_ECODE_FAILED; |
6480cd1b EL |
200 | } |
201 | ||
202 | /** | |
203 | * @} | |
204 | */ |