PPP Server - pppd and Network Address Translation (NAT)
Point to Point Protocol (PPP) used for remote serial connections using TCP protocol. It offers authentication, Network Address Translation (NAT) and PPP.
A TCP server talks across Ethernet to communicate to other nodes on a subnet when using IPV4. If the node that a user wants to communicate with is not on the subnet, then the packet must be routed through the gateway node to the remote node. To perform this routing, the gateway node must do Network Address Translation or NAT.
•An outgoing call has its address, and potentially its port, remapped to another address on the external network, cyclic
redundancy check (CRC) recalculated and the packet sent on.
•The gateway remembers this translation and allows return packets to be routed back to the sender. This is the basic scheme that allows users to setup subnets on IPV4 and route packets back and forth.
If a subnet is remote, and is connected via a wireless link like GPRS or another wireless or wireline link, two more aspects come into play. First the protocol that TCP users to communicate over serial lines must be used. It is called PPP. Now the node which hosts PPP and another Ethernet becomes a gateway and must use NAT as well to route packets out the correct ports.
The second aspect of setting up a serial link is that there is generally a modem which must be initialized and connected first before the network is setup. If there is a link failure on the modem, this link failure must be communicated and the modem restarted before the link can be brought back up. The command set that is used is called an AT command set for historical reasons. These are relatively standard commands widely used for a variety of purposes in communications and industrial control.
Pppd requires a configuration record for initialization related to authentication which is setup by an interface call or with direct structure modification
The basic features of the pppd and NAT interface are:
•PPP
•NAT including Port Address Translation (PAT)
•Good authentication
•A PPP implementation architecture which enables modem configuration using AT or other commands before PPP is run.
•Off the shelf authentication
•User management features
Configuration of the pppd Server
There are a number of things that have to be setup for pppd and NAT to work correctly.
•NAT requires the specification of the routing tables to properly route packets. This requires both the IP address of the
outgoing interface as well as the IP addresses on the local networks which must be mapped to it.
•Pppd requires a configuration record for initialization related to authentication which is setup by an interface call or with
direct structure modification.
The basic steps for configuration are:
1. Call pppInit() to start the server.
2. Modify values in the ppp_settings struct either directly or use the pppSetAuth() to setup the authorization part of the
struct.
3. Use pppOpen to start the link using a particular serial line, specifying the callback function for error handling.
4. Now you should be up and running.
To get the status while the link is up, call pppIOCTL().
If errors occur, the callback function is called with and error code and the application programmer must take appropriate action. If an error occurs because the link went down, the connection is automatically closed. To close the link at any time, simply call pppClose() and then let the server know that the link has been closed using pppSIGHUP().
ppp_settings
/*************************
*** PUBLIC DEFINITIONS ***
*************************/
/* Error codes. */
#define PPPERR_NONE 0 /* No error. */
#define PPPERR_PARAM -1 /* Invalid parameter. */
#define PPPERR_OPEN -2 /* Unable to open PPP session. */
#define PPPERR_DEVICE -3 /* Invalid I/O device for PPP. */
#define PPPERR_ALLOC -4 /* Unable to allocate resources. */
#define PPPERR_USER -5 /* User interrupt. */
#define PPPERR_CONNECT -6 /* Connection lost. */
#define PPPERR_AUTHFAIL -7 /* Failed authentication challenge. */
#define PPPERR_PROTOCOL -8 /* Failed to meet protocol. */
/* Flow control codes */
#define FLCTL_NONE 0
#define FLCTL_XONXOFF 1
#define FLCTL_CRTSCTS 2
/************************
*** PUBLIC DATA TYPES ***
************************/
struct ppp_settings {
u_int disable_defaultip : 1; /* Don't use hostname for default IP addrs */
u_int auth_required : 1; /* Peer is required to authenticate */
u_int explicit_remote : 1; /* remote_name specified with remotename opt */
u_int refuse_pap : 1; /* Don't wanna auth. ourselves with PAP */
u_int refuse_chap : 1; /* Don't wanna auth. ourselves with CHAP */
u_int usehostname : 1; /* Use hostname for our_name */
u_int usepeerdns : 1; /* Ask peer for DNS adds */
u_short idle_time_limit; /* Shut down link if idle for this long */
int maxconnect; /* Maximum connect time (seconds) */
char flow_ctl; /* Flow control mode */
char user[MAXNAMELEN + 1]; /* Username for PAP */
char passwd[MAXSECRETLEN + 1]; /* Password for PAP, secret for CHAP */
char our_name[MAXNAMELEN + 1]; /* Our name for authentication purposes */
char remote_name[MAXNAMELEN + 1]; /* Peer's name for authentication */
};
/*****************************
*** PUBLIC DATA STRUCTURES ***
*****************************/
extern struct ppp_settings ppp_settings;
/* Warning: Using PPPAUTHTYPE_ANY might have security consequences.
* RFC 1994 says don't mix PAP and CHAP – see demo code for details */
enum pppAuthType {
PPPAUTHTYPE_NONE,
PPPAUTHTYPE_ANY,
PPPAUTHTYPE_PAP,
PPPAUTHTYPE_CHAP
};
void pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd);
First the user must initialize the PPP subsystem by calling
void pppInit(void);
Make sure all settings are correct including the setting of the flow control.
After the settings are correct, the PPP link can be opened using the command and the reference to the given I/O device. This initializes the PPP control block but does not attempt to negotiate the LCP session. It returns a new PPP connection descriptor on success or an error code (negative) on failure.
int pppOpen(char *tty_name, void (*linkStatusCB)(void *ctx, int errCode, void *arg), void *linkStatusCtx);
To get the status use PPP IOCTL commands. Get the up status - 0 for down, non-zero for up. The argument must point to an int.
#define PPPCTLG_UPSTATUS 100 /* Get the up status - 0 down else up */
#define PPPCTLS_ERRCODE 101 /* Set the error code */
#define PPPCTLG_ERRCODE 102 /* Get the error code */
#define PPPCTLG_FD 103 /* Get the fd associated with the ppp */
int pppIOCTL(int pd, int cmd, void *arg);
To close the connection, simply close it and handle the events that come back via the callback. The user can close a PPP connection and release the descriptor calling pppClose with the pd returned from the open. Any outstanding packets in the queues are dropped. pppClose returns 0 on success and an error code on failure.
int pppClose(int pd);
The pppClose cleans up the data structures but the user still must indicate to the PPP process that the line has disconnected.
void pppSigHUP(int pd);
If a connection is closed, the callback gets called twice in most cases. The following is a summary of the conditions and the errors generated.
Closed by the remote side.
ISP normally closes ppp connection. User will get 2 callback events:
- PPPERR_CONNECT (Connection lost)
- PPPERR_PROTOCOL (Failed to meet protocol)
Closed by user command
User closed connection by command pppClose().User will get 2 callback events:
- PPPERR_CONNECT (Connection lost)
- PPPERR_USER (User interrupt)
Closed by user max time connect expired.
User can set max time for the connection:
ppp_settings.maxconnect = 10; // time in seconds
User will get 2 callback events:
- PPPERR_CONNECT (Connection lost)
- PPPERR_PROTOCOL (Failed to meet protocol)
Closed by link failure
pppd automatically sends control link packets. If pppd does not get a response from remote side, the user will get 2 callback events:
- PPPERR_CONNECT (Connection lost)
- PPPERR_PROTOCOL (Failed to meet protocol)
After any close event, the user can open the modem tty port and try to reconnect to ISP using AT commands. Other useful commands include returning the Maximum Transmission Unit for the given PPP connection.
u_int pppMTU(int pd);
Another call provides all IP addresses for PPP connection.
struct ppp_addrs {
struct in_addr our_ipaddr, his_ipaddr, netmask, dns1, dns2;
};
struct ppp_addrs *pppGetIP(int pd);
Example for Startup:
void start_pppd(void)
{
int err;
pppInit();
//here are we can directly modify settings for ppp connection
//ppp_settings.maxconnect = 10;
pppSetAuth(PPPAUTHTYPE_PAP, "user", "passwd");
//pppSetAuth(PPPAUTHTYPE_NONE, "", "");
//without authentic INSERT LINE: ppp_settings.flow_ctl = FLCTL_XONXOFF;
//open connection
err = pppOpen("/dev/ttyS1", ppp_callback, (void*)1);
… }
Example to Get the up status:
// 0 for down, non-zero for up, using API
int res;
pppIOCTL(ppp_id, PPPCTLG_UPSTATUS, &res);
Example to Handle errors
Use call back functions for error parsing and handling
void ppp_callback(void *ctx, int errCode, void *arg)
{
xprintf("ppp_callback: ctx: 0x%x, err: 0x%x, arg: 0x%x", (int)ctx, errCode, (int)arg);
//Here user will parse errors and made handling for them
switch (errCode)
{
case PPPERR_NONE:
//got connection
break;
case PPPERR_CONNECT:
//connection lost
break;
case PPPERR_OPEN:
//unable to open PPP session
break
//and so on see Error codes description above
}
}
Example to Close / interrupt ppp connection
int pppClose(int pd);
void pppSigHUP(int pd);
Configuration of the NAT Mapping
The NAT functionality is provided by conditional compilation within the TCP server. It adds an extra table to the TCP server to hold this information.
After they have established the PPP connection the user can setup nat rules. The user can setup for PPP interface IP address mapping and port rules. For example, a host could have ab Ethernet IP address of 192.168.16.199 and the ISP connected by PPP IP could use the address 71.72.5.7. First, to activate NAT the user must switch on NAT using the command ipl_enable(). After this the user must set two NAT rules using natIOCTL().
map pp0 192.168.16.199/24 -> 71.72.5.7/32 portmap tcp/udp 40000:60000
map pp0 192.168.16.199/24 -> 71.72.5.7/32
All TCP and UDP packets will change their source IP to 71.72.5.7 and source port to port from range 40000:60000. All other packets (ICMP) will change only their source IP. If the user wants, they can change only IP for all packets and leave the ports unchanged.
map pp0 192.168.16.199/24 -> 71.72.5.7/32
After the PPP connection is closed the user can clear the NAT rules by stopping NAT using ipl_disable(). The user can restart NAT using ipl_enable() and set up new rules with the new PPP IP.
Example Using NAT
#define ETHIP "192.168.16.199"
#define PPPIP inet_ntoa(addr->our_ipaddr)
char *nat_rules[] =
{
"map pp0 %s/24 -> %s/32 portmap tcp/udp 40000:60000",
"map pp0 %s/24 -> %s/32",
};
char line[128];
void nat_setup(void)
{
ipnat_t*np;
int i;
struct ppp_addrs *addr;
// Start ip filter
ipl_enable();
// Get PPP address (interface pp0)
addr = pppGetIP(ppp_id);
// Parse nat rules and put its to ip filter
for(i=0; i < sizeof(nat_rules)/sizeof(char*); i++)
{
memset(line, 0, sizeof(line));
sprintf(line, nat_rules[i], ETHIP, PPPIP);
printf("parse nat rule: '%s'\n", line);
if (!(np = parse(line)))
{
fprintf(stderr, "syntax error in \"%s\"\n", line);
}
else if (nat_ioctl(SIOCADNAT, (caddr_t)np) == -1)
perror("ioctl(SIOCADNAT)");
}
}
Quick Start Guide for more details PPPd with NAT