nat_cmd.c revision 50479
1/*-
2 * The code in this file was written by Eivind Eklund <perhaps@yes.no>,
3 * who places it in the public domain without restriction.
4 *
5 * $FreeBSD: head/usr.sbin/ppp/nat_cmd.c 50479 1999-08-28 01:35:59Z peter $
6 */
7
8#include <sys/param.h>
9#include <netinet/in.h>
10#include <arpa/inet.h>
11#include <netdb.h>
12#include <netinet/in_systm.h>
13#include <netinet/in.h>
14#include <netinet/ip.h>
15#include <sys/un.h>
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <termios.h>
21
22#ifdef __FreeBSD__
23#include <alias.h>
24#else
25#include "alias.h"
26#endif
27#include "layer.h"
28#include "proto.h"
29#include "defs.h"
30#include "command.h"
31#include "log.h"
32#include "alias_cmd.h"
33#include "descriptor.h"
34#include "prompt.h"
35#include "timer.h"
36#include "fsm.h"
37#include "slcompress.h"
38#include "throughput.h"
39#include "iplist.h"
40#include "mbuf.h"
41#include "lqr.h"
42#include "hdlc.h"
43#include "ipcp.h"
44#include "lcp.h"
45#include "ccp.h"
46#include "link.h"
47#include "mp.h"
48#include "filter.h"
49#ifndef NORADIUS
50#include "radius.h"
51#endif
52#include "bundle.h"
53
54
55static int StrToAddr(const char *, struct in_addr *);
56static int StrToPortRange(const char *, u_short *, u_short *, const char *);
57static int StrToAddrAndPort(const char *, struct in_addr *, u_short *,
58                            u_short *, const char *);
59
60static void
61lowhigh(u_short *a, u_short *b)
62{
63  if (a > b) {
64    u_short c;
65
66    c = *b;
67    *b = *a;
68    *a = c;
69  }
70}
71
72int
73nat_RedirectPort(struct cmdargs const *arg)
74{
75  if (!arg->bundle->NatEnabled) {
76    prompt_Printf(arg->prompt, "Alias not enabled\n");
77    return 1;
78  } else if (arg->argc == arg->argn + 3 || arg->argc == arg->argn + 4) {
79    char proto_constant;
80    const char *proto;
81    struct in_addr localaddr;
82    u_short hlocalport, llocalport;
83    struct in_addr aliasaddr;
84    u_short haliasport, laliasport;
85    struct in_addr remoteaddr;
86    u_short hremoteport, lremoteport;
87    struct alias_link *link;
88    int error;
89
90    proto = arg->argv[arg->argn];
91    if (strcmp(proto, "tcp") == 0) {
92      proto_constant = IPPROTO_TCP;
93    } else if (strcmp(proto, "udp") == 0) {
94      proto_constant = IPPROTO_UDP;
95    } else {
96      prompt_Printf(arg->prompt, "port redirect: protocol must be"
97                    " tcp or udp\n");
98      return -1;
99    }
100
101    error = StrToAddrAndPort(arg->argv[arg->argn+1], &localaddr, &llocalport,
102                             &hlocalport, proto);
103    if (error) {
104      prompt_Printf(arg->prompt, "nat port: error reading localaddr:port\n");
105      return -1;
106    }
107
108    error = StrToPortRange(arg->argv[arg->argn+2], &laliasport, &haliasport,
109                           proto);
110    if (error) {
111      prompt_Printf(arg->prompt, "nat port: error reading alias port\n");
112      return -1;
113    }
114    aliasaddr.s_addr = INADDR_ANY;
115
116    if (arg->argc == arg->argn + 4) {
117      error = StrToAddrAndPort(arg->argv[arg->argn+3], &remoteaddr,
118                               &lremoteport, &hremoteport, proto);
119      if (error) {
120        prompt_Printf(arg->prompt, "nat port: error reading "
121                      "remoteaddr:port\n");
122        return -1;
123      }
124    } else {
125      remoteaddr.s_addr = INADDR_ANY;
126      lremoteport = hremoteport = 0;
127    }
128
129    lowhigh(&llocalport, &hlocalport);
130    lowhigh(&laliasport, &haliasport);
131    lowhigh(&lremoteport, &hremoteport);
132
133    if (haliasport - laliasport != hlocalport - llocalport) {
134      prompt_Printf(arg->prompt, "nat port: local & alias port ranges "
135                    "are not equal\n");
136      return -1;
137    }
138
139    if (hremoteport && hremoteport - lremoteport != hlocalport - llocalport) {
140      prompt_Printf(arg->prompt, "nat port: local & remote port ranges "
141                    "are not equal\n");
142      return -1;
143    }
144
145    while (laliasport <= haliasport) {
146      link = PacketAliasRedirectPort(localaddr, htons(llocalport),
147				     remoteaddr, htons(lremoteport),
148                                     aliasaddr, htons(laliasport),
149				     proto_constant);
150
151      if (link == NULL) {
152        prompt_Printf(arg->prompt, "nat port: %d: error %d\n", laliasport,
153                      error);
154        return 1;
155      }
156      llocalport++;
157      laliasport++;
158      if (hremoteport)
159        lremoteport++;
160    }
161
162    return 0;
163  }
164
165  return -1;
166}
167
168
169int
170nat_RedirectAddr(struct cmdargs const *arg)
171{
172  if (!arg->bundle->NatEnabled) {
173    prompt_Printf(arg->prompt, "nat not enabled\n");
174    return 1;
175  } else if (arg->argc == arg->argn+2) {
176    int error;
177    struct in_addr localaddr, aliasaddr;
178    struct alias_link *link;
179
180    error = StrToAddr(arg->argv[arg->argn], &localaddr);
181    if (error) {
182      prompt_Printf(arg->prompt, "address redirect: invalid local address\n");
183      return 1;
184    }
185    error = StrToAddr(arg->argv[arg->argn+1], &aliasaddr);
186    if (error) {
187      prompt_Printf(arg->prompt, "address redirect: invalid alias address\n");
188      prompt_Printf(arg->prompt, "Usage: nat %s %s\n", arg->cmd->name,
189                    arg->cmd->syntax);
190      return 1;
191    }
192    link = PacketAliasRedirectAddr(localaddr, aliasaddr);
193    if (link == NULL) {
194      prompt_Printf(arg->prompt, "address redirect: packet aliasing"
195                    " engine error\n");
196      prompt_Printf(arg->prompt, "Usage: nat %s %s\n", arg->cmd->name,
197                    arg->cmd->syntax);
198    }
199  } else
200    return -1;
201
202  return 0;
203}
204
205
206static int
207StrToAddr(const char *str, struct in_addr *addr)
208{
209  struct hostent *hp;
210
211  if (inet_aton(str, addr))
212    return 0;
213
214  hp = gethostbyname(str);
215  if (!hp) {
216    log_Printf(LogWARN, "StrToAddr: Unknown host %s.\n", str);
217    return -1;
218  }
219  *addr = *((struct in_addr *) hp->h_addr);
220  return 0;
221}
222
223
224static int
225StrToPort(const char *str, u_short *port, const char *proto)
226{
227  struct servent *sp;
228  char *end;
229
230  *port = strtol(str, &end, 10);
231  if (*end != '\0') {
232    sp = getservbyname(str, proto);
233    if (sp == NULL) {
234      log_Printf(LogWARN, "StrToAddr: Unknown port or service %s/%s.\n",
235	        str, proto);
236      return -1;
237    }
238    *port = ntohs(sp->s_port);
239  }
240
241  return 0;
242}
243
244static int
245StrToPortRange(const char *str, u_short *low, u_short *high, const char *proto)
246{
247  char *minus;
248  int res;
249
250  minus = strchr(str, '-');
251  if (minus)
252    *minus = '\0';		/* Cheat the const-ness ! */
253
254  res = StrToPort(str, low, proto);
255
256  if (minus)
257    *minus = '-';		/* Cheat the const-ness ! */
258
259  if (res == 0) {
260    if (minus)
261      res = StrToPort(minus + 1, high, proto);
262    else
263      *high = *low;
264  }
265
266  return res;
267}
268
269static int
270StrToAddrAndPort(const char *str, struct in_addr *addr, u_short *low,
271                 u_short *high, const char *proto)
272{
273  char *colon;
274  int res;
275
276  colon = strchr(str, ':');
277  if (!colon) {
278    log_Printf(LogWARN, "StrToAddrAndPort: %s is missing port number.\n", str);
279    return -1;
280  }
281
282  *colon = '\0';		/* Cheat the const-ness ! */
283  res = StrToAddr(str, addr);
284  *colon = ':';			/* Cheat the const-ness ! */
285  if (res != 0)
286    return -1;
287
288  return StrToPortRange(colon + 1, low, high, proto);
289}
290
291int
292nat_ProxyRule(struct cmdargs const *arg)
293{
294  char cmd[LINE_LEN];
295  int f, pos;
296  size_t len;
297
298  if (arg->argn >= arg->argc)
299    return -1;
300
301  for (f = arg->argn, pos = 0; f < arg->argc; f++) {
302    len = strlen(arg->argv[f]);
303    if (sizeof cmd - pos < len + (f ? 1 : 0))
304      break;
305    if (f)
306      cmd[pos++] = ' ';
307    strcpy(cmd + pos, arg->argv[f]);
308    pos += len;
309  }
310
311  return PacketAliasProxyRule(cmd);
312}
313
314int
315nat_Pptp(struct cmdargs const *arg)
316{
317  struct in_addr addr;
318
319  if (arg->argc == arg->argn) {
320    addr.s_addr = INADDR_NONE;
321    PacketAliasPptp(addr);
322    return 0;
323  }
324
325  if (arg->argc != arg->argn + 1)
326    return -1;
327
328  addr = GetIpAddr(arg->argv[arg->argn]);
329  if (addr.s_addr == INADDR_NONE) {
330    log_Printf(LogWARN, "%s: invalid address\n", arg->argv[arg->argn]);
331    return 1;
332  }
333
334  PacketAliasPptp(addr);
335  return 0;
336}
337
338static struct mbuf *
339nat_PadMbuf(struct mbuf *bp, int type)
340{
341  struct mbuf **last;
342  int len;
343
344  mbuf_SetType(bp, type);
345  for (last = &bp, len = 0; *last != NULL; last = &(*last)->next)
346    len += (*last)->cnt;
347
348  len = MAX_MRU - len;
349  *last = mbuf_Alloc(len, type);
350
351  return bp;
352}
353
354static struct mbuf *
355nat_LayerPush(struct bundle *bundle, struct link *l, struct mbuf *bp,
356                int pri, u_short *proto)
357{
358  if (!bundle->NatEnabled || *proto != PROTO_IP)
359    return bp;
360
361  log_Printf(LogDEBUG, "nat_LayerPush: PROTO_IP -> PROTO_IP\n");
362  bp = mbuf_Contiguous(nat_PadMbuf(bp, MB_NATOUT));
363  PacketAliasOut(MBUF_CTOP(bp), bp->cnt);
364  bp->cnt = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
365
366  return bp;
367}
368
369static struct mbuf *
370nat_LayerPull(struct bundle *bundle, struct link *l, struct mbuf *bp,
371                u_short *proto)
372{
373  struct ip *pip, *piip;
374  int ret, len;
375  struct mbuf **last;
376  char *fptr;
377
378  if (!bundle->NatEnabled || *proto != PROTO_IP)
379    return bp;
380
381  log_Printf(LogDEBUG, "nat_LayerPull: PROTO_IP -> PROTO_IP\n");
382  bp = mbuf_Contiguous(nat_PadMbuf(bp, MB_NATIN));
383  pip = (struct ip *)MBUF_CTOP(bp);
384  piip = (struct ip *)((char *)pip + (pip->ip_hl << 2));
385
386  if (pip->ip_p == IPPROTO_IGMP ||
387      (pip->ip_p == IPPROTO_IPIP && IN_CLASSD(ntohl(piip->ip_dst.s_addr))))
388    return bp;
389
390  ret = PacketAliasIn(MBUF_CTOP(bp), bp->cnt);
391
392  bp->cnt = ntohs(pip->ip_len);
393  if (bp->cnt > MAX_MRU) {
394    log_Printf(LogWARN, "nat_LayerPull: Problem with IP header length\n");
395    mbuf_Free(bp);
396    return NULL;
397  }
398
399  switch (ret) {
400    case PKT_ALIAS_OK:
401      break;
402
403    case PKT_ALIAS_UNRESOLVED_FRAGMENT:
404      /* Save the data for later */
405      fptr = malloc(bp->cnt);
406      bp = mbuf_Read(bp, fptr, bp->cnt);
407      PacketAliasSaveFragment(fptr);
408      break;
409
410    case PKT_ALIAS_FOUND_HEADER_FRAGMENT:
411      /* Fetch all the saved fragments and chain them on the end of `bp' */
412      last = &bp->pnext;
413      while ((fptr = PacketAliasGetFragment(MBUF_CTOP(bp))) != NULL) {
414	PacketAliasFragmentIn(MBUF_CTOP(bp), fptr);
415        len = ntohs(((struct ip *)fptr)->ip_len);
416        *last = mbuf_Alloc(len, MB_NATIN);
417        memcpy(MBUF_CTOP(*last), fptr, len);
418        free(fptr);
419        last = &(*last)->pnext;
420      }
421      break;
422
423    default:
424      mbuf_Free(bp);
425      bp = NULL;
426      break;
427  }
428
429  return bp;
430}
431
432struct layer natlayer =
433  { LAYER_NAT, "nat", nat_LayerPush, nat_LayerPull };
434