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