blob: d94a59a5f13dce2cc6cf86c99239294f058292f0 [file] [log] [blame]
Chia-chi Yeh706e5672011-06-02 14:26:57 -07001/*-
2 * Copyright (c) 2001 Charles Mott <cm@linktel.net>
3 * Brian Somers <brian@Awfulhak.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: src/usr.sbin/ppp/nat_cmd.c,v 1.62.10.1.4.1 2010/12/21 17:10:29 kensmith Exp $
28 */
29
30#include <sys/param.h>
31#include <netinet/in.h>
32#include <arpa/inet.h>
33#include <netdb.h>
34#include <netinet/in_systm.h>
35#include <netinet/in.h>
36#include <netinet/ip.h>
37#include <sys/socket.h>
38#include <sys/un.h>
39
40#include <stdarg.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <termios.h>
45
46#ifdef LOCALNAT
47#include "alias.h"
48#else
49#include <alias.h>
50#endif
51
52#include "layer.h"
53#include "proto.h"
54#include "defs.h"
55#include "command.h"
56#include "log.h"
57#include "nat_cmd.h"
58#include "descriptor.h"
59#include "prompt.h"
60#include "timer.h"
61#include "fsm.h"
62#include "slcompress.h"
63#include "throughput.h"
64#include "iplist.h"
65#include "mbuf.h"
66#include "lqr.h"
67#include "hdlc.h"
68#include "ncpaddr.h"
69#include "ip.h"
70#include "ipcp.h"
71#include "ipv6cp.h"
72#include "lcp.h"
73#include "ccp.h"
74#include "link.h"
75#include "mp.h"
76#include "filter.h"
77#ifndef NORADIUS
78#include "radius.h"
79#endif
80#include "ncp.h"
81#include "bundle.h"
82
83
84#define NAT_EXTRABUF (13)
85
86static int StrToAddr(const char *, struct in_addr *);
87static int StrToPortRange(const char *, u_short *, u_short *, const char *);
88static int StrToAddrAndPort(const char *, struct in_addr *, u_short *,
89 u_short *, const char *);
90
91static void
92lowhigh(u_short *a, u_short *b)
93{
94 if (a > b) {
95 u_short c;
96
97 c = *b;
98 *b = *a;
99 *a = c;
100 }
101}
102
103int
104nat_RedirectPort(struct cmdargs const *arg)
105{
106 if (!arg->bundle->NatEnabled) {
107 prompt_Printf(arg->prompt, "Alias not enabled\n");
108 return 1;
109 } else if (arg->argc == arg->argn + 3 || arg->argc == arg->argn + 4) {
110 char proto_constant;
111 const char *proto;
112 struct in_addr localaddr;
113 u_short hlocalport, llocalport;
114 struct in_addr aliasaddr;
115 u_short haliasport, laliasport;
116 struct in_addr remoteaddr;
117 u_short hremoteport, lremoteport;
118 struct alias_link *link;
119 int error;
120
121 proto = arg->argv[arg->argn];
122 if (strcmp(proto, "tcp") == 0) {
123 proto_constant = IPPROTO_TCP;
124 } else if (strcmp(proto, "udp") == 0) {
125 proto_constant = IPPROTO_UDP;
126 } else {
127 prompt_Printf(arg->prompt, "port redirect: protocol must be"
128 " tcp or udp\n");
129 return -1;
130 }
131
132 error = StrToAddrAndPort(arg->argv[arg->argn+1], &localaddr, &llocalport,
133 &hlocalport, proto);
134 if (error) {
135 prompt_Printf(arg->prompt, "nat port: error reading localaddr:port\n");
136 return -1;
137 }
138
139 error = StrToPortRange(arg->argv[arg->argn+2], &laliasport, &haliasport,
140 proto);
141 if (error) {
142 prompt_Printf(arg->prompt, "nat port: error reading alias port\n");
143 return -1;
144 }
145 aliasaddr.s_addr = INADDR_ANY;
146
147 if (arg->argc == arg->argn + 4) {
148 error = StrToAddrAndPort(arg->argv[arg->argn+3], &remoteaddr,
149 &lremoteport, &hremoteport, proto);
150 if (error) {
151 prompt_Printf(arg->prompt, "nat port: error reading "
152 "remoteaddr:port\n");
153 return -1;
154 }
155 } else {
156 remoteaddr.s_addr = INADDR_ANY;
157 lremoteport = hremoteport = 0;
158 }
159
160 lowhigh(&llocalport, &hlocalport);
161 lowhigh(&laliasport, &haliasport);
162 lowhigh(&lremoteport, &hremoteport);
163
164 if (haliasport - laliasport != hlocalport - llocalport) {
165 prompt_Printf(arg->prompt, "nat port: local & alias port ranges "
166 "are not equal\n");
167 return -1;
168 }
169
170 if (hremoteport && hremoteport - lremoteport != hlocalport - llocalport) {
171 prompt_Printf(arg->prompt, "nat port: local & remote port ranges "
172 "are not equal\n");
173 return -1;
174 }
175
176 do {
177 link = PacketAliasRedirectPort(localaddr, htons(llocalport),
178 remoteaddr, htons(lremoteport),
179 aliasaddr, htons(laliasport),
180 proto_constant);
181
182 if (link == NULL) {
183 prompt_Printf(arg->prompt, "nat port: %d: error %d\n", laliasport,
184 error);
185 return 1;
186 }
187 llocalport++;
188 if (hremoteport)
189 lremoteport++;
190 } while (laliasport++ < haliasport);
191
192 return 0;
193 }
194
195 return -1;
196}
197
198
199int
200nat_RedirectAddr(struct cmdargs const *arg)
201{
202 if (!arg->bundle->NatEnabled) {
203 prompt_Printf(arg->prompt, "nat not enabled\n");
204 return 1;
205 } else if (arg->argc == arg->argn+2) {
206 int error;
207 struct in_addr localaddr, aliasaddr;
208 struct alias_link *link;
209
210 error = StrToAddr(arg->argv[arg->argn], &localaddr);
211 if (error) {
212 prompt_Printf(arg->prompt, "address redirect: invalid local address\n");
213 return 1;
214 }
215 error = StrToAddr(arg->argv[arg->argn+1], &aliasaddr);
216 if (error) {
217 prompt_Printf(arg->prompt, "address redirect: invalid alias address\n");
218 prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
219 arg->cmd->syntax);
220 return 1;
221 }
222 link = PacketAliasRedirectAddr(localaddr, aliasaddr);
223 if (link == NULL) {
224 prompt_Printf(arg->prompt, "address redirect: packet aliasing"
225 " engine error\n");
226 prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
227 arg->cmd->syntax);
228 }
229 } else
230 return -1;
231
232 return 0;
233}
234
235
236int
237nat_RedirectProto(struct cmdargs const *arg)
238{
239 if (!arg->bundle->NatEnabled) {
240 prompt_Printf(arg->prompt, "nat not enabled\n");
241 return 1;
242 } else if (arg->argc >= arg->argn + 2 && arg->argc <= arg->argn + 4) {
243 struct in_addr localIP, publicIP, remoteIP;
244 struct alias_link *link;
245 struct protoent *pe;
246 int error;
247 unsigned len;
248
249 len = strlen(arg->argv[arg->argn]);
250 if (len == 0) {
251 prompt_Printf(arg->prompt, "proto redirect: invalid protocol\n");
252 return 1;
253 }
254 if (strspn(arg->argv[arg->argn], "01234567") == len)
255 pe = getprotobynumber(atoi(arg->argv[arg->argn]));
256 else
257 pe = getprotobyname(arg->argv[arg->argn]);
258 if (pe == NULL) {
259 prompt_Printf(arg->prompt, "proto redirect: invalid protocol\n");
260 return 1;
261 }
262
263 error = StrToAddr(arg->argv[arg->argn + 1], &localIP);
264 if (error) {
265 prompt_Printf(arg->prompt, "proto redirect: invalid src address\n");
266 return 1;
267 }
268
269 if (arg->argc >= arg->argn + 3) {
270 error = StrToAddr(arg->argv[arg->argn + 2], &publicIP);
271 if (error) {
272 prompt_Printf(arg->prompt, "proto redirect: invalid alias address\n");
273 prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
274 arg->cmd->syntax);
275 return 1;
276 }
277 } else
278 publicIP.s_addr = INADDR_ANY;
279
280 if (arg->argc == arg->argn + 4) {
281 error = StrToAddr(arg->argv[arg->argn + 2], &remoteIP);
282 if (error) {
283 prompt_Printf(arg->prompt, "proto redirect: invalid dst address\n");
284 prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
285 arg->cmd->syntax);
286 return 1;
287 }
288 } else
289 remoteIP.s_addr = INADDR_ANY;
290
291 link = PacketAliasRedirectProto(localIP, remoteIP, publicIP, pe->p_proto);
292 if (link == NULL) {
293 prompt_Printf(arg->prompt, "proto redirect: packet aliasing"
294 " engine error\n");
295 prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
296 arg->cmd->syntax);
297 }
298 } else
299 return -1;
300
301 return 0;
302}
303
304
305static int
306StrToAddr(const char *str, struct in_addr *addr)
307{
308 struct hostent *hp;
309
310 if (inet_aton(str, addr))
311 return 0;
312
313 hp = gethostbyname(str);
314 if (!hp) {
315 log_Printf(LogWARN, "StrToAddr: Unknown host %s.\n", str);
316 return -1;
317 }
318 *addr = *((struct in_addr *) hp->h_addr);
319 return 0;
320}
321
322
323static int
324StrToPort(const char *str, u_short *port, const char *proto)
325{
326 struct servent *sp;
327 char *end;
328
329 *port = strtol(str, &end, 10);
330 if (*end != '\0') {
331 sp = getservbyname(str, proto);
332 if (sp == NULL) {
333 log_Printf(LogWARN, "StrToAddr: Unknown port or service %s/%s.\n",
334 str, proto);
335 return -1;
336 }
337 *port = ntohs(sp->s_port);
338 }
339
340 return 0;
341}
342
343static int
344StrToPortRange(const char *str, u_short *low, u_short *high, const char *proto)
345{
346 char *minus;
347 int res;
348
349 minus = strchr(str, '-');
350 if (minus)
351 *minus = '\0'; /* Cheat the const-ness ! */
352
353 res = StrToPort(str, low, proto);
354
355 if (minus)
356 *minus = '-'; /* Cheat the const-ness ! */
357
358 if (res == 0) {
359 if (minus)
360 res = StrToPort(minus + 1, high, proto);
361 else
362 *high = *low;
363 }
364
365 return res;
366}
367
368static int
369StrToAddrAndPort(const char *str, struct in_addr *addr, u_short *low,
370 u_short *high, const char *proto)
371{
372 char *colon;
373 int res;
374
375 colon = strchr(str, ':');
376 if (!colon) {
377 log_Printf(LogWARN, "StrToAddrAndPort: %s is missing port number.\n", str);
378 return -1;
379 }
380
381 *colon = '\0'; /* Cheat the const-ness ! */
382 res = StrToAddr(str, addr);
383 *colon = ':'; /* Cheat the const-ness ! */
384 if (res != 0)
385 return -1;
386
387 return StrToPortRange(colon + 1, low, high, proto);
388}
389
390int
391nat_ProxyRule(struct cmdargs const *arg)
392{
393 char cmd[LINE_LEN];
394 int f, pos;
395 size_t len;
396
397 if (arg->argn >= arg->argc)
398 return -1;
399
400 for (f = arg->argn, pos = 0; f < arg->argc; f++) {
401 len = strlen(arg->argv[f]);
402 if (sizeof cmd - pos < len + (len ? 1 : 0))
403 break;
404 if (len)
405 cmd[pos++] = ' ';
406 strcpy(cmd + pos, arg->argv[f]);
407 pos += len;
408 }
409
410 return PacketAliasProxyRule(cmd);
411}
412
413int
414nat_SetTarget(struct cmdargs const *arg)
415{
416 struct in_addr addr;
417
418 if (arg->argc == arg->argn) {
419 addr.s_addr = INADDR_ANY;
420 PacketAliasSetTarget(addr);
421 return 0;
422 }
423
424 if (arg->argc != arg->argn + 1)
425 return -1;
426
427 if (!strcasecmp(arg->argv[arg->argn], "MYADDR")) {
428 addr.s_addr = INADDR_ANY;
429 PacketAliasSetTarget(addr);
430 return 0;
431 }
432
433 addr = GetIpAddr(arg->argv[arg->argn]);
434 if (addr.s_addr == INADDR_NONE) {
435 log_Printf(LogWARN, "%s: invalid address\n", arg->argv[arg->argn]);
436 return 1;
437 }
438
439 PacketAliasSetTarget(addr);
440 return 0;
441}
442
443#ifndef NO_FW_PUNCH
444int
445nat_PunchFW(struct cmdargs const *arg)
446{
447 char *end;
448 long base, count;
449
450 if (arg->argc == arg->argn) {
451 PacketAliasSetMode(0, PKT_ALIAS_PUNCH_FW);
452 return 0;
453 }
454
455 if (arg->argc != arg->argn + 2)
456 return -1;
457
458 base = strtol(arg->argv[arg->argn], &end, 10);
459 if (*end != '\0' || base < 0)
460 return -1;
461
462 count = strtol(arg->argv[arg->argn + 1], &end, 10);
463 if (*end != '\0' || count < 0)
464 return -1;
465
466 PacketAliasSetFWBase(base, count);
467 PacketAliasSetMode(PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW);
468
469 return 0;
470}
471#endif
472
473int
474nat_SkinnyPort(struct cmdargs const *arg)
475{
476 char *end;
477 long port;
478
479 if (arg->argc == arg->argn) {
480 PacketAliasSetSkinnyPort(0);
481 return 0;
482 }
483
484 if (arg->argc != arg->argn + 1)
485 return -1;
486
487 port = strtol(arg->argv[arg->argn], &end, 10);
488 if (*end != '\0' || port < 0)
489 return -1;
490
491 PacketAliasSetSkinnyPort(port);
492
493 return 0;
494}
495
496static struct mbuf *
497nat_LayerPush(struct bundle *bundle, struct link *l __unused, struct mbuf *bp,
498 int pri __unused, u_short *proto)
499{
500 if (!bundle->NatEnabled || *proto != PROTO_IP)
501 return bp;
502
503 log_Printf(LogDEBUG, "nat_LayerPush: PROTO_IP -> PROTO_IP\n");
504 m_settype(bp, MB_NATOUT);
505 /* Ensure there's a bit of extra buffer for the NAT code... */
506 bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
507 PacketAliasOut(MBUF_CTOP(bp), bp->m_len);
508 bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
509
510 return bp;
511}
512
513static struct mbuf *
514nat_LayerPull(struct bundle *bundle, struct link *l __unused, struct mbuf *bp,
515 u_short *proto)
516{
517 static int gfrags;
518 int ret, len, nfrags;
519 struct mbuf **last;
520 char *fptr;
521
522 if (!bundle->NatEnabled || *proto != PROTO_IP)
523 return bp;
524
525 log_Printf(LogDEBUG, "nat_LayerPull: PROTO_IP -> PROTO_IP\n");
526 m_settype(bp, MB_NATIN);
527 /* Ensure there's a bit of extra buffer for the NAT code... */
528 bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
529 ret = PacketAliasIn(MBUF_CTOP(bp), bp->m_len);
530
531 bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
532 if (bp->m_len > MAX_MRU) {
533 log_Printf(LogWARN, "nat_LayerPull: Problem with IP header length (%lu)\n",
534 (unsigned long)bp->m_len);
535 m_freem(bp);
536 return NULL;
537 }
538
539 switch (ret) {
540 case PKT_ALIAS_OK:
541 break;
542
543 case PKT_ALIAS_UNRESOLVED_FRAGMENT:
544 /* Save the data for later */
545 if ((fptr = malloc(bp->m_len)) == NULL) {
546 log_Printf(LogWARN, "nat_LayerPull: Dropped unresolved fragment -"
547 " out of memory!\n");
548 m_freem(bp);
549 bp = NULL;
550 } else {
551 bp = mbuf_Read(bp, fptr, bp->m_len);
552 PacketAliasSaveFragment(fptr);
553 log_Printf(LogDEBUG, "Store another frag (%lu) - now %d\n",
554 (unsigned long)((struct ip *)fptr)->ip_id, ++gfrags);
555 }
556 break;
557
558 case PKT_ALIAS_FOUND_HEADER_FRAGMENT:
559 /* Fetch all the saved fragments and chain them on the end of `bp' */
560 last = &bp->m_nextpkt;
561 nfrags = 0;
562 while ((fptr = PacketAliasGetFragment(MBUF_CTOP(bp))) != NULL) {
563 nfrags++;
564 PacketAliasFragmentIn(MBUF_CTOP(bp), fptr);
565 len = ntohs(((struct ip *)fptr)->ip_len);
566 *last = m_get(len, MB_NATIN);
567 memcpy(MBUF_CTOP(*last), fptr, len);
568 free(fptr);
569 last = &(*last)->m_nextpkt;
570 }
571 gfrags -= nfrags;
572 log_Printf(LogDEBUG, "Found a frag header (%lu) - plus %d more frags (no"
573 "w %d)\n", (unsigned long)((struct ip *)MBUF_CTOP(bp))->ip_id,
574 nfrags, gfrags);
575 break;
576
577 case PKT_ALIAS_IGNORED:
578 if (PacketAliasSetMode(0, 0) & PKT_ALIAS_DENY_INCOMING) {
579 log_Printf(LogTCPIP, "NAT engine denied data:\n");
580 m_freem(bp);
581 bp = NULL;
582 } else if (log_IsKept(LogTCPIP)) {
583 log_Printf(LogTCPIP, "NAT engine ignored data:\n");
584 PacketCheck(bundle, AF_INET, MBUF_CTOP(bp), bp->m_len, NULL,
585 NULL, NULL);
586 }
587 break;
588
589 default:
590 log_Printf(LogWARN, "nat_LayerPull: Dropped a packet (%d)....\n", ret);
591 m_freem(bp);
592 bp = NULL;
593 break;
594 }
595
596 return bp;
597}
598
599struct layer natlayer =
600 { LAYER_NAT, "nat", nat_LayerPush, nat_LayerPull };