nat_cmd.c revision 195772
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 195772 2009-07-19 19:01:30Z brian $
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
91extern struct libalias *la;
92
93static void
94lowhigh(u_short *a, u_short *b)
95{
96  if (a > b) {
97    u_short c;
98
99    c = *b;
100    *b = *a;
101    *a = c;
102  }
103}
104
105int
106nat_RedirectPort(struct cmdargs const *arg)
107{
108  if (!arg->bundle->NatEnabled) {
109    prompt_Printf(arg->prompt, "Alias not enabled\n");
110    return 1;
111  } else if (arg->argc == arg->argn + 3 || arg->argc == arg->argn + 4) {
112    char proto_constant;
113    const char *proto;
114    struct in_addr localaddr;
115    u_short hlocalport, llocalport;
116    struct in_addr aliasaddr;
117    u_short haliasport, laliasport;
118    struct in_addr remoteaddr;
119    u_short hremoteport, lremoteport;
120    struct alias_link *link;
121    int error;
122
123    proto = arg->argv[arg->argn];
124    if (strcmp(proto, "tcp") == 0) {
125      proto_constant = IPPROTO_TCP;
126    } else if (strcmp(proto, "udp") == 0) {
127      proto_constant = IPPROTO_UDP;
128    } else {
129      prompt_Printf(arg->prompt, "port redirect: protocol must be"
130                    " tcp or udp\n");
131      return -1;
132    }
133
134    error = StrToAddrAndPort(arg->argv[arg->argn+1], &localaddr, &llocalport,
135                             &hlocalport, proto);
136    if (error) {
137      prompt_Printf(arg->prompt, "nat port: error reading localaddr:port\n");
138      return -1;
139    }
140
141    error = StrToPortRange(arg->argv[arg->argn+2], &laliasport, &haliasport,
142                           proto);
143    if (error) {
144      prompt_Printf(arg->prompt, "nat port: error reading alias port\n");
145      return -1;
146    }
147    aliasaddr.s_addr = INADDR_ANY;
148
149    if (arg->argc == arg->argn + 4) {
150      error = StrToAddrAndPort(arg->argv[arg->argn+3], &remoteaddr,
151                               &lremoteport, &hremoteport, proto);
152      if (error) {
153        prompt_Printf(arg->prompt, "nat port: error reading "
154                      "remoteaddr:port\n");
155        return -1;
156      }
157    } else {
158      remoteaddr.s_addr = INADDR_ANY;
159      lremoteport = hremoteport = 0;
160    }
161
162    lowhigh(&llocalport, &hlocalport);
163    lowhigh(&laliasport, &haliasport);
164    lowhigh(&lremoteport, &hremoteport);
165
166    if (haliasport - laliasport != hlocalport - llocalport) {
167      prompt_Printf(arg->prompt, "nat port: local & alias port ranges "
168                    "are not equal\n");
169      return -1;
170    }
171
172    if (hremoteport && hremoteport - lremoteport != hlocalport - llocalport) {
173      prompt_Printf(arg->prompt, "nat port: local & remote port ranges "
174                    "are not equal\n");
175      return -1;
176    }
177
178    do {
179      link = LibAliasRedirectPort(la, localaddr, htons(llocalport),
180				     remoteaddr, htons(lremoteport),
181                                     aliasaddr, htons(laliasport),
182				     proto_constant);
183
184      if (link == NULL) {
185        prompt_Printf(arg->prompt, "nat port: %d: error %d\n", laliasport,
186                      error);
187        return 1;
188      }
189      llocalport++;
190      if (hremoteport)
191        lremoteport++;
192    } while (laliasport++ < haliasport);
193
194    return 0;
195  }
196
197  return -1;
198}
199
200
201int
202nat_RedirectAddr(struct cmdargs const *arg)
203{
204  if (!arg->bundle->NatEnabled) {
205    prompt_Printf(arg->prompt, "nat not enabled\n");
206    return 1;
207  } else if (arg->argc == arg->argn+2) {
208    int error;
209    struct in_addr localaddr, aliasaddr;
210    struct alias_link *link;
211
212    error = StrToAddr(arg->argv[arg->argn], &localaddr);
213    if (error) {
214      prompt_Printf(arg->prompt, "address redirect: invalid local address\n");
215      return 1;
216    }
217    error = StrToAddr(arg->argv[arg->argn+1], &aliasaddr);
218    if (error) {
219      prompt_Printf(arg->prompt, "address redirect: invalid alias address\n");
220      prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
221                    arg->cmd->syntax);
222      return 1;
223    }
224    link = LibAliasRedirectAddr(la, localaddr, aliasaddr);
225    if (link == NULL) {
226      prompt_Printf(arg->prompt, "address redirect: packet aliasing"
227                    " engine error\n");
228      prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
229                    arg->cmd->syntax);
230    }
231  } else
232    return -1;
233
234  return 0;
235}
236
237
238int
239nat_RedirectProto(struct cmdargs const *arg)
240{
241  if (!arg->bundle->NatEnabled) {
242    prompt_Printf(arg->prompt, "nat not enabled\n");
243    return 1;
244  } else if (arg->argc >= arg->argn + 2 && arg->argc <= arg->argn + 4) {
245    struct in_addr localIP, publicIP, remoteIP;
246    struct alias_link *link;
247    struct protoent *pe;
248    int error;
249    unsigned len;
250
251    len = strlen(arg->argv[arg->argn]);
252    if (len == 0) {
253      prompt_Printf(arg->prompt, "proto redirect: invalid protocol\n");
254      return 1;
255    }
256    if (strspn(arg->argv[arg->argn], "01234567") == len)
257      pe = getprotobynumber(atoi(arg->argv[arg->argn]));
258    else
259      pe = getprotobyname(arg->argv[arg->argn]);
260    if (pe == NULL) {
261      prompt_Printf(arg->prompt, "proto redirect: invalid protocol\n");
262      return 1;
263    }
264
265    error = StrToAddr(arg->argv[arg->argn + 1], &localIP);
266    if (error) {
267      prompt_Printf(arg->prompt, "proto redirect: invalid src address\n");
268      return 1;
269    }
270
271    if (arg->argc >= arg->argn + 3) {
272      error = StrToAddr(arg->argv[arg->argn + 2], &publicIP);
273      if (error) {
274        prompt_Printf(arg->prompt, "proto redirect: invalid alias address\n");
275        prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
276                      arg->cmd->syntax);
277        return 1;
278      }
279    } else
280      publicIP.s_addr = INADDR_ANY;
281
282    if (arg->argc == arg->argn + 4) {
283      error = StrToAddr(arg->argv[arg->argn + 2], &remoteIP);
284      if (error) {
285        prompt_Printf(arg->prompt, "proto redirect: invalid dst address\n");
286        prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
287                      arg->cmd->syntax);
288        return 1;
289      }
290    } else
291      remoteIP.s_addr = INADDR_ANY;
292
293    link = LibAliasRedirectProto(la, localIP, remoteIP, publicIP, pe->p_proto);
294    if (link == NULL) {
295      prompt_Printf(arg->prompt, "proto redirect: packet aliasing"
296                    " engine error\n");
297      prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
298                    arg->cmd->syntax);
299    }
300  } else
301    return -1;
302
303  return 0;
304}
305
306
307static int
308StrToAddr(const char *str, struct in_addr *addr)
309{
310  struct hostent *hp;
311
312  if (inet_aton(str, addr))
313    return 0;
314
315  hp = gethostbyname(str);
316  if (!hp) {
317    log_Printf(LogWARN, "StrToAddr: Unknown host %s.\n", str);
318    return -1;
319  }
320  *addr = *((struct in_addr *) hp->h_addr);
321  return 0;
322}
323
324
325static int
326StrToPort(const char *str, u_short *port, const char *proto)
327{
328  struct servent *sp;
329  char *end;
330
331  *port = strtol(str, &end, 10);
332  if (*end != '\0') {
333    sp = getservbyname(str, proto);
334    if (sp == NULL) {
335      log_Printf(LogWARN, "StrToAddr: Unknown port or service %s/%s.\n",
336	        str, proto);
337      return -1;
338    }
339    *port = ntohs(sp->s_port);
340  }
341
342  return 0;
343}
344
345static int
346StrToPortRange(const char *str, u_short *low, u_short *high, const char *proto)
347{
348  char *minus;
349  int res;
350
351  minus = strchr(str, '-');
352  if (minus)
353    *minus = '\0';		/* Cheat the const-ness ! */
354
355  res = StrToPort(str, low, proto);
356
357  if (minus)
358    *minus = '-';		/* Cheat the const-ness ! */
359
360  if (res == 0) {
361    if (minus)
362      res = StrToPort(minus + 1, high, proto);
363    else
364      *high = *low;
365  }
366
367  return res;
368}
369
370static int
371StrToAddrAndPort(const char *str, struct in_addr *addr, u_short *low,
372                 u_short *high, const char *proto)
373{
374  char *colon;
375  int res;
376
377  colon = strchr(str, ':');
378  if (!colon) {
379    log_Printf(LogWARN, "StrToAddrAndPort: %s is missing port number.\n", str);
380    return -1;
381  }
382
383  *colon = '\0';		/* Cheat the const-ness ! */
384  res = StrToAddr(str, addr);
385  *colon = ':';			/* Cheat the const-ness ! */
386  if (res != 0)
387    return -1;
388
389  return StrToPortRange(colon + 1, low, high, proto);
390}
391
392int
393nat_ProxyRule(struct cmdargs const *arg)
394{
395  char cmd[LINE_LEN];
396  int f, pos;
397  size_t len;
398
399  if (arg->argn >= arg->argc)
400    return -1;
401
402  for (f = arg->argn, pos = 0; f < arg->argc; f++) {
403    len = strlen(arg->argv[f]);
404    if (sizeof cmd - pos < len + (len ? 1 : 0))
405      break;
406    if (len)
407      cmd[pos++] = ' ';
408    strcpy(cmd + pos, arg->argv[f]);
409    pos += len;
410  }
411
412  return LibAliasProxyRule(la, cmd);
413}
414
415int
416nat_SetTarget(struct cmdargs const *arg)
417{
418  struct in_addr addr;
419
420  if (arg->argc == arg->argn) {
421    addr.s_addr = INADDR_ANY;
422    LibAliasSetTarget(la, addr);
423    return 0;
424  }
425
426  if (arg->argc != arg->argn + 1)
427    return -1;
428
429  if (!strcasecmp(arg->argv[arg->argn], "MYADDR")) {
430    addr.s_addr = INADDR_ANY;
431    LibAliasSetTarget(la, addr);
432    return 0;
433  }
434
435  addr = GetIpAddr(arg->argv[arg->argn]);
436  if (addr.s_addr == INADDR_NONE) {
437    log_Printf(LogWARN, "%s: invalid address\n", arg->argv[arg->argn]);
438    return 1;
439  }
440
441  LibAliasSetTarget(la, addr);
442  return 0;
443}
444
445#ifndef NO_FW_PUNCH
446int
447nat_PunchFW(struct cmdargs const *arg)
448{
449  char *end;
450  long base, count;
451
452  if (arg->argc == arg->argn) {
453    LibAliasSetMode(la, 0, PKT_ALIAS_PUNCH_FW);
454    return 0;
455  }
456
457  if (arg->argc != arg->argn + 2)
458    return -1;
459
460  base = strtol(arg->argv[arg->argn], &end, 10);
461  if (*end != '\0' || base < 0)
462    return -1;
463
464  count = strtol(arg->argv[arg->argn + 1], &end, 10);
465  if (*end != '\0' || count < 0)
466    return -1;
467
468  LibAliasSetFWBase(la, base, count);
469  LibAliasSetMode(la, PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW);
470
471  return 0;
472}
473#endif
474
475int
476nat_SkinnyPort(struct cmdargs const *arg)
477{
478  char *end;
479  long port;
480
481  if (arg->argc == arg->argn) {
482    LibAliasSetSkinnyPort(la, 0);
483    return 0;
484  }
485
486  if (arg->argc != arg->argn + 1)
487    return -1;
488
489  port = strtol(arg->argv[arg->argn], &end, 10);
490  if (*end != '\0' || port < 0)
491    return -1;
492
493  LibAliasSetSkinnyPort(la, port);
494
495  return 0;
496}
497
498static struct mbuf *
499nat_LayerPush(struct bundle *bundle, struct link *l __unused, struct mbuf *bp,
500                int pri __unused, u_short *proto)
501{
502  if (!bundle->NatEnabled || *proto != PROTO_IP)
503    return bp;
504
505  log_Printf(LogDEBUG, "nat_LayerPush: PROTO_IP -> PROTO_IP\n");
506  m_settype(bp, MB_NATOUT);
507  /* Ensure there's a bit of extra buffer for the NAT code... */
508  bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
509  LibAliasOut(la, MBUF_CTOP(bp), bp->m_len);
510  bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
511
512  return bp;
513}
514
515static struct mbuf *
516nat_LayerPull(struct bundle *bundle, struct link *l __unused, struct mbuf *bp,
517                u_short *proto)
518{
519  static int gfrags;
520  int ret, len, nfrags;
521  struct mbuf **last;
522  char *fptr;
523
524  if (!bundle->NatEnabled || *proto != PROTO_IP)
525    return bp;
526
527  log_Printf(LogDEBUG, "nat_LayerPull: PROTO_IP -> PROTO_IP\n");
528  m_settype(bp, MB_NATIN);
529  /* Ensure there's a bit of extra buffer for the NAT code... */
530  bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
531  ret = LibAliasIn(la, MBUF_CTOP(bp), bp->m_len);
532
533  bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
534  if (bp->m_len > MAX_MRU) {
535    log_Printf(LogWARN, "nat_LayerPull: Problem with IP header length (%lu)\n",
536               (unsigned long)bp->m_len);
537    m_freem(bp);
538    return NULL;
539  }
540
541  switch (ret) {
542    case PKT_ALIAS_OK:
543      break;
544
545    case PKT_ALIAS_UNRESOLVED_FRAGMENT:
546      /* Save the data for later */
547      if ((fptr = malloc(bp->m_len)) == NULL) {
548	log_Printf(LogWARN, "nat_LayerPull: Dropped unresolved fragment -"
549		   " out of memory!\n");
550	m_freem(bp);
551	bp = NULL;
552      } else {
553	bp = mbuf_Read(bp, fptr, bp->m_len);
554	LibAliasSaveFragment(la, fptr);
555	log_Printf(LogDEBUG, "Store another frag (%lu) - now %d\n",
556		   (unsigned long)((struct ip *)fptr)->ip_id, ++gfrags);
557      }
558      break;
559
560    case PKT_ALIAS_FOUND_HEADER_FRAGMENT:
561      /* Fetch all the saved fragments and chain them on the end of `bp' */
562      last = &bp->m_nextpkt;
563      nfrags = 0;
564      while ((fptr = LibAliasGetFragment(la, MBUF_CTOP(bp))) != NULL) {
565        nfrags++;
566        LibAliasFragmentIn(la, MBUF_CTOP(bp), fptr);
567        len = ntohs(((struct ip *)fptr)->ip_len);
568        *last = m_get(len, MB_NATIN);
569        memcpy(MBUF_CTOP(*last), fptr, len);
570        free(fptr);
571        last = &(*last)->m_nextpkt;
572      }
573      gfrags -= nfrags;
574      log_Printf(LogDEBUG, "Found a frag header (%lu) - plus %d more frags (no"
575                 "w %d)\n", (unsigned long)((struct ip *)MBUF_CTOP(bp))->ip_id,
576                 nfrags, gfrags);
577      break;
578
579    case PKT_ALIAS_IGNORED:
580      if (LibAliasSetMode(la, 0, 0) & PKT_ALIAS_DENY_INCOMING) {
581        log_Printf(LogTCPIP, "NAT engine denied data:\n");
582        m_freem(bp);
583        bp = NULL;
584      } else if (log_IsKept(LogTCPIP)) {
585        log_Printf(LogTCPIP, "NAT engine ignored data:\n");
586        PacketCheck(bundle, AF_INET, MBUF_CTOP(bp), bp->m_len, NULL,
587                    NULL, NULL);
588      }
589      break;
590
591    default:
592      log_Printf(LogWARN, "nat_LayerPull: Dropped a packet (%d)....\n", ret);
593      m_freem(bp);
594      bp = NULL;
595      break;
596  }
597
598  return bp;
599}
600
601struct layer natlayer =
602  { LAYER_NAT, "nat", nat_LayerPush, nat_LayerPull };
603