nat_cmd.c revision 95258
1/*-
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: head/usr.sbin/ppp/nat_cmd.c 95258 2002-04-22 13:44:47Z des $
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 <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <termios.h>
44
45#ifdef LOCALNAT
46#include "alias.h"
47#else
48#include <alias.h>
49#endif
50
51#include "layer.h"
52#include "proto.h"
53#include "defs.h"
54#include "command.h"
55#include "log.h"
56#include "nat_cmd.h"
57#include "descriptor.h"
58#include "prompt.h"
59#include "timer.h"
60#include "fsm.h"
61#include "slcompress.h"
62#include "throughput.h"
63#include "iplist.h"
64#include "mbuf.h"
65#include "lqr.h"
66#include "hdlc.h"
67#include "ncpaddr.h"
68#include "ip.h"
69#include "ipcp.h"
70#include "ipv6cp.h"
71#include "lcp.h"
72#include "ccp.h"
73#include "link.h"
74#include "mp.h"
75#include "filter.h"
76#ifndef NORADIUS
77#include "radius.h"
78#endif
79#include "ncp.h"
80#include "bundle.h"
81
82
83#define NAT_EXTRABUF (13)
84
85static int StrToAddr(const char *, struct in_addr *);
86static int StrToPortRange(const char *, u_short *, u_short *, const char *);
87static int StrToAddrAndPort(const char *, struct in_addr *, u_short *,
88                            u_short *, const char *);
89
90static void
91lowhigh(u_short *a, u_short *b)
92{
93  if (a > b) {
94    u_short c;
95
96    c = *b;
97    *b = *a;
98    *a = c;
99  }
100}
101
102int
103nat_RedirectPort(struct cmdargs const *arg)
104{
105  if (!arg->bundle->NatEnabled) {
106    prompt_Printf(arg->prompt, "Alias not enabled\n");
107    return 1;
108  } else if (arg->argc == arg->argn + 3 || arg->argc == arg->argn + 4) {
109    char proto_constant;
110    const char *proto;
111    struct in_addr localaddr;
112    u_short hlocalport, llocalport;
113    struct in_addr aliasaddr;
114    u_short haliasport, laliasport;
115    struct in_addr remoteaddr;
116    u_short hremoteport, lremoteport;
117    struct alias_link *link;
118    int error;
119
120    proto = arg->argv[arg->argn];
121    if (strcmp(proto, "tcp") == 0) {
122      proto_constant = IPPROTO_TCP;
123    } else if (strcmp(proto, "udp") == 0) {
124      proto_constant = IPPROTO_UDP;
125    } else {
126      prompt_Printf(arg->prompt, "port redirect: protocol must be"
127                    " tcp or udp\n");
128      return -1;
129    }
130
131    error = StrToAddrAndPort(arg->argv[arg->argn+1], &localaddr, &llocalport,
132                             &hlocalport, proto);
133    if (error) {
134      prompt_Printf(arg->prompt, "nat port: error reading localaddr:port\n");
135      return -1;
136    }
137
138    error = StrToPortRange(arg->argv[arg->argn+2], &laliasport, &haliasport,
139                           proto);
140    if (error) {
141      prompt_Printf(arg->prompt, "nat port: error reading alias port\n");
142      return -1;
143    }
144    aliasaddr.s_addr = INADDR_ANY;
145
146    if (arg->argc == arg->argn + 4) {
147      error = StrToAddrAndPort(arg->argv[arg->argn+3], &remoteaddr,
148                               &lremoteport, &hremoteport, proto);
149      if (error) {
150        prompt_Printf(arg->prompt, "nat port: error reading "
151                      "remoteaddr:port\n");
152        return -1;
153      }
154    } else {
155      remoteaddr.s_addr = INADDR_ANY;
156      lremoteport = hremoteport = 0;
157    }
158
159    lowhigh(&llocalport, &hlocalport);
160    lowhigh(&laliasport, &haliasport);
161    lowhigh(&lremoteport, &hremoteport);
162
163    if (haliasport - laliasport != hlocalport - llocalport) {
164      prompt_Printf(arg->prompt, "nat port: local & alias port ranges "
165                    "are not equal\n");
166      return -1;
167    }
168
169    if (hremoteport && hremoteport - lremoteport != hlocalport - llocalport) {
170      prompt_Printf(arg->prompt, "nat port: local & remote port ranges "
171                    "are not equal\n");
172      return -1;
173    }
174
175    while (laliasport <= haliasport) {
176      link = PacketAliasRedirectPort(localaddr, htons(llocalport),
177				     remoteaddr, htons(lremoteport),
178                                     aliasaddr, htons(laliasport),
179				     proto_constant);
180
181      if (link == NULL) {
182        prompt_Printf(arg->prompt, "nat port: %d: error %d\n", laliasport,
183                      error);
184        return 1;
185      }
186      llocalport++;
187      laliasport++;
188      if (hremoteport)
189        lremoteport++;
190    }
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, len;
247
248    len = strlen(arg->argv[arg->argn]);
249    if (len == 0) {
250      prompt_Printf(arg->prompt, "proto redirect: invalid protocol\n");
251      return 1;
252    }
253    if (strspn(arg->argv[arg->argn], "01234567") == len)
254      pe = getprotobynumber(atoi(arg->argv[arg->argn]));
255    else
256      pe = getprotobyname(arg->argv[arg->argn]);
257    if (pe == NULL) {
258      prompt_Printf(arg->prompt, "proto redirect: invalid protocol\n");
259      return 1;
260    }
261
262    error = StrToAddr(arg->argv[arg->argn + 1], &localIP);
263    if (error) {
264      prompt_Printf(arg->prompt, "proto redirect: invalid src address\n");
265      return 1;
266    }
267
268    if (arg->argc >= arg->argn + 3) {
269      error = StrToAddr(arg->argv[arg->argn + 2], &publicIP);
270      if (error) {
271        prompt_Printf(arg->prompt, "proto redirect: invalid alias address\n");
272        prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
273                      arg->cmd->syntax);
274        return 1;
275      }
276    } else
277      publicIP.s_addr = INADDR_ANY;
278
279    if (arg->argc == arg->argn + 4) {
280      error = StrToAddr(arg->argv[arg->argn + 2], &remoteIP);
281      if (error) {
282        prompt_Printf(arg->prompt, "proto redirect: invalid dst address\n");
283        prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
284                      arg->cmd->syntax);
285        return 1;
286      }
287    } else
288      remoteIP.s_addr = INADDR_ANY;
289
290    link = PacketAliasRedirectProto(localIP, remoteIP, publicIP, pe->p_proto);
291    if (link == NULL) {
292      prompt_Printf(arg->prompt, "proto redirect: packet aliasing"
293                    " engine error\n");
294      prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
295                    arg->cmd->syntax);
296    }
297  } else
298    return -1;
299
300  return 0;
301}
302
303
304static int
305StrToAddr(const char *str, struct in_addr *addr)
306{
307  struct hostent *hp;
308
309  if (inet_aton(str, addr))
310    return 0;
311
312  hp = gethostbyname(str);
313  if (!hp) {
314    log_Printf(LogWARN, "StrToAddr: Unknown host %s.\n", str);
315    return -1;
316  }
317  *addr = *((struct in_addr *) hp->h_addr);
318  return 0;
319}
320
321
322static int
323StrToPort(const char *str, u_short *port, const char *proto)
324{
325  struct servent *sp;
326  char *end;
327
328  *port = strtol(str, &end, 10);
329  if (*end != '\0') {
330    sp = getservbyname(str, proto);
331    if (sp == NULL) {
332      log_Printf(LogWARN, "StrToAddr: Unknown port or service %s/%s.\n",
333	        str, proto);
334      return -1;
335    }
336    *port = ntohs(sp->s_port);
337  }
338
339  return 0;
340}
341
342static int
343StrToPortRange(const char *str, u_short *low, u_short *high, const char *proto)
344{
345  char *minus;
346  int res;
347
348  minus = strchr(str, '-');
349  if (minus)
350    *minus = '\0';		/* Cheat the const-ness ! */
351
352  res = StrToPort(str, low, proto);
353
354  if (minus)
355    *minus = '-';		/* Cheat the const-ness ! */
356
357  if (res == 0) {
358    if (minus)
359      res = StrToPort(minus + 1, high, proto);
360    else
361      *high = *low;
362  }
363
364  return res;
365}
366
367static int
368StrToAddrAndPort(const char *str, struct in_addr *addr, u_short *low,
369                 u_short *high, const char *proto)
370{
371  char *colon;
372  int res;
373
374  colon = strchr(str, ':');
375  if (!colon) {
376    log_Printf(LogWARN, "StrToAddrAndPort: %s is missing port number.\n", str);
377    return -1;
378  }
379
380  *colon = '\0';		/* Cheat the const-ness ! */
381  res = StrToAddr(str, addr);
382  *colon = ':';			/* Cheat the const-ness ! */
383  if (res != 0)
384    return -1;
385
386  return StrToPortRange(colon + 1, low, high, proto);
387}
388
389int
390nat_ProxyRule(struct cmdargs const *arg)
391{
392  char cmd[LINE_LEN];
393  int f, pos;
394  size_t len;
395
396  if (arg->argn >= arg->argc)
397    return -1;
398
399  for (f = arg->argn, pos = 0; f < arg->argc; f++) {
400    len = strlen(arg->argv[f]);
401    if (sizeof cmd - pos < len + (len ? 1 : 0))
402      break;
403    if (len)
404      cmd[pos++] = ' ';
405    strcpy(cmd + pos, arg->argv[f]);
406    pos += len;
407  }
408
409  return PacketAliasProxyRule(cmd);
410}
411
412int
413nat_SetTarget(struct cmdargs const *arg)
414{
415  struct in_addr addr;
416
417  if (arg->argc == arg->argn) {
418    addr.s_addr = INADDR_ANY;
419    PacketAliasSetTarget(addr);
420    return 0;
421  }
422
423  if (arg->argc != arg->argn + 1)
424    return -1;
425
426  if (!strcasecmp(arg->argv[arg->argn], "MYADDR")) {
427    addr.s_addr = INADDR_ANY;
428    PacketAliasSetTarget(addr);
429    return 0;
430  }
431
432  addr = GetIpAddr(arg->argv[arg->argn]);
433  if (addr.s_addr == INADDR_NONE) {
434    log_Printf(LogWARN, "%s: invalid address\n", arg->argv[arg->argn]);
435    return 1;
436  }
437
438  PacketAliasSetTarget(addr);
439  return 0;
440}
441
442#ifndef NO_FW_PUNCH
443int
444nat_PunchFW(struct cmdargs const *arg)
445{
446  char *end;
447  long base, count;
448
449  if (arg->argc == arg->argn) {
450    PacketAliasSetMode(0, PKT_ALIAS_PUNCH_FW);
451    return 0;
452  }
453
454  if (arg->argc != arg->argn + 2)
455    return -1;
456
457  base = strtol(arg->argv[arg->argn], &end, 10);
458  if (*end != '\0' || base < 0)
459    return -1;
460
461  count = strtol(arg->argv[arg->argn + 1], &end, 10);
462  if (*end != '\0' || count < 0)
463    return -1;
464
465  PacketAliasSetFWBase(base, count);
466  PacketAliasSetMode(PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW);
467
468  return 0;
469}
470#endif
471
472static struct mbuf *
473nat_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp,
474                int pri, u_short *proto)
475{
476  if (!bundle->NatEnabled || *proto != PROTO_IP)
477    return bp;
478
479  log_Printf(LogDEBUG, "nat_LayerPush: PROTO_IP -> PROTO_IP\n");
480  m_settype(bp, MB_NATOUT);
481  /* Ensure there's a bit of extra buffer for the NAT code... */
482  bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
483  PacketAliasOut(MBUF_CTOP(bp), bp->m_len);
484  bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
485
486  return bp;
487}
488
489static struct mbuf *
490nat_LayerPull(struct bundle *bundle, struct link *l, struct mbuf *bp,
491                u_short *proto)
492{
493  static int gfrags;
494  int ret, len, nfrags;
495  struct mbuf **last;
496  char *fptr;
497
498  if (!bundle->NatEnabled || *proto != PROTO_IP)
499    return bp;
500
501  log_Printf(LogDEBUG, "nat_LayerPull: PROTO_IP -> PROTO_IP\n");
502  m_settype(bp, MB_NATIN);
503  /* Ensure there's a bit of extra buffer for the NAT code... */
504  bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
505  ret = PacketAliasIn(MBUF_CTOP(bp), bp->m_len);
506
507  bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
508  if (bp->m_len > MAX_MRU) {
509    log_Printf(LogWARN, "nat_LayerPull: Problem with IP header length (%lu)\n",
510               (unsigned long)bp->m_len);
511    m_freem(bp);
512    return NULL;
513  }
514
515  switch (ret) {
516    case PKT_ALIAS_OK:
517      break;
518
519    case PKT_ALIAS_UNRESOLVED_FRAGMENT:
520      /* Save the data for later */
521      fptr = malloc(bp->m_len);
522      bp = mbuf_Read(bp, fptr, bp->m_len);
523      PacketAliasSaveFragment(fptr);
524      log_Printf(LogDEBUG, "Store another frag (%lu) - now %d\n",
525                 (unsigned long)((struct ip *)fptr)->ip_id, ++gfrags);
526      break;
527
528    case PKT_ALIAS_FOUND_HEADER_FRAGMENT:
529      /* Fetch all the saved fragments and chain them on the end of `bp' */
530      last = &bp->m_nextpkt;
531      nfrags = 0;
532      while ((fptr = PacketAliasGetFragment(MBUF_CTOP(bp))) != NULL) {
533        nfrags++;
534        PacketAliasFragmentIn(MBUF_CTOP(bp), fptr);
535        len = ntohs(((struct ip *)fptr)->ip_len);
536        *last = m_get(len, MB_NATIN);
537        memcpy(MBUF_CTOP(*last), fptr, len);
538        free(fptr);
539        last = &(*last)->m_nextpkt;
540      }
541      gfrags -= nfrags;
542      log_Printf(LogDEBUG, "Found a frag header (%lu) - plus %d more frags (no"
543                 "w %d)\n", (unsigned long)((struct ip *)MBUF_CTOP(bp))->ip_id,
544                 nfrags, gfrags);
545      break;
546
547    case PKT_ALIAS_IGNORED:
548      if (PacketAliasSetMode(0, 0) & PKT_ALIAS_DENY_INCOMING) {
549        log_Printf(LogTCPIP, "NAT engine denied data:\n");
550        m_freem(bp);
551        bp = NULL;
552      } else if (log_IsKept(LogTCPIP)) {
553        log_Printf(LogTCPIP, "NAT engine ignored data:\n");
554        PacketCheck(bundle, AF_INET, MBUF_CTOP(bp), bp->m_len, NULL,
555                    NULL, NULL);
556      }
557      break;
558
559    default:
560      log_Printf(LogWARN, "nat_LayerPull: Dropped a packet (%d)....\n", ret);
561      m_freem(bp);
562      bp = NULL;
563      break;
564  }
565
566  return bp;
567}
568
569struct layer natlayer =
570  { LAYER_NAT, "nat", nat_LayerPush, nat_LayerPull };
571