Skip to content

Commit

Permalink
pppd: Add replacedefaultroute option (#200)
Browse files Browse the repository at this point in the history
Adds an option to pppd to control whether to replace existing default routes
when using the 'defaultroute' option.

If defaultroute and replacedefaultroute are both set, pppd replaces an existing
default route with the new default route. The old default route is restored when
the connection is taken down.

Signed-off-by: Samuel Thibault <[email protected]>

Co-authored-by: Chris Boot <[email protected]>
  • Loading branch information
sthibaul and bootc authored Jan 2, 2021
1 parent 965e7fd commit d0ccb87
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 21 deletions.
37 changes: 29 additions & 8 deletions pppd/ipcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,15 @@ static option_t ipcp_option_list[] = {
"disable defaultroute option", OPT_ALIAS | OPT_A2CLR,
&ipcp_wantoptions[0].default_route },

#ifdef __linux__
{ "replacedefaultroute", o_bool,
&ipcp_wantoptions[0].replace_default_route,
"Replace default route", OPT_PRIV | 1
},
{ "noreplacedefaultroute", o_bool,
&ipcp_wantoptions[0].replace_default_route,
"Do not replace default route", 0 },
#endif
{ "proxyarp", o_bool, &ipcp_wantoptions[0].proxy_arp,
"Add proxy ARP entry", OPT_ENABLE|1, &ipcp_allowoptions[0].proxy_arp },
{ "noproxyarp", o_bool, &ipcp_allowoptions[0].proxy_arp,
Expand Down Expand Up @@ -269,7 +278,7 @@ struct protent ipcp_protent = {
ip_active_pkt
};

static void ipcp_clear_addrs (int, u_int32_t, u_int32_t);
static void ipcp_clear_addrs (int, u_int32_t, u_int32_t, bool);
static void ipcp_script (char *, int); /* Run an up/down script */
static void ipcp_script_done (void *);

Expand Down Expand Up @@ -1716,7 +1725,8 @@ ip_demand_conf(int u)
if (!sifnpmode(u, PPP_IP, NPMODE_QUEUE))
return 0;
if (wo->default_route)
if (sifdefaultroute(u, wo->ouraddr, wo->hisaddr))
if (sifdefaultroute(u, wo->ouraddr, wo->hisaddr,
wo->replace_default_route))
default_route_set[u] = 1;
if (wo->proxy_arp)
if (sifproxyarp(u, wo->hisaddr))
Expand Down Expand Up @@ -1804,7 +1814,8 @@ ipcp_up(fsm *f)
*/
if (demand) {
if (go->ouraddr != wo->ouraddr || ho->hisaddr != wo->hisaddr) {
ipcp_clear_addrs(f->unit, wo->ouraddr, wo->hisaddr);
ipcp_clear_addrs(f->unit, wo->ouraddr, wo->hisaddr,
wo->replace_default_route);
if (go->ouraddr != wo->ouraddr) {
warn("Local IP address changed to %I", go->ouraddr);
script_setenv("OLDIPLOCAL", ip_ntoa(wo->ouraddr), 0);
Expand All @@ -1829,7 +1840,8 @@ ipcp_up(fsm *f)

/* assign a default route through the interface if required */
if (ipcp_wantoptions[f->unit].default_route)
if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr))
if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr,
wo->replace_default_route))
default_route_set[f->unit] = 1;

/* Make a proxy ARP entry if requested. */
Expand Down Expand Up @@ -1888,7 +1900,8 @@ ipcp_up(fsm *f)

/* assign a default route through the interface if required */
if (ipcp_wantoptions[f->unit].default_route)
if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr))
if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr,
wo->replace_default_route))
default_route_set[f->unit] = 1;

/* Make a proxy ARP entry if requested. */
Expand Down Expand Up @@ -1965,7 +1978,7 @@ ipcp_down(fsm *f)
sifnpmode(f->unit, PPP_IP, NPMODE_DROP);
sifdown(f->unit);
ipcp_clear_addrs(f->unit, ipcp_gotoptions[f->unit].ouraddr,
ipcp_hisoptions[f->unit].hisaddr);
ipcp_hisoptions[f->unit].hisaddr, 0);
}

/* Execute the ip-down script */
Expand All @@ -1981,13 +1994,21 @@ ipcp_down(fsm *f)
* proxy arp entries, etc.
*/
static void
ipcp_clear_addrs(int unit, u_int32_t ouraddr, u_int32_t hisaddr)
ipcp_clear_addrs(int unit, u_int32_t ouraddr, u_int32_t hisaddr, bool replacedefaultroute)
{
if (proxy_arp_set[unit]) {
cifproxyarp(unit, hisaddr);
proxy_arp_set[unit] = 0;
}
if (default_route_set[unit]) {
/* If replacedefaultroute, sifdefaultroute will be called soon
* with replacedefaultroute set and that will overwrite the current
* default route. This is the case only when doing demand, otherwise
* during demand, this cifdefaultroute would restore the old default
* route which is not what we want in this case. In the non-demand
* case, we'll delete the default route and restore the old if there
* is one saved by an sifdefaultroute with replacedefaultroute.
*/
if (!replacedefaultroute && default_route_set[unit]) {
cifdefaultroute(unit, ouraddr, hisaddr);
default_route_set[unit] = 0;
}
Expand Down
1 change: 1 addition & 0 deletions pppd/ipcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ typedef struct ipcp_options {
bool old_addrs; /* Use old (IP-Addresses) option? */
bool req_addr; /* Ask peer to send IP address? */
bool default_route; /* Assign default route through interface? */
bool replace_default_route; /* Replace default route through interface? */
bool proxy_arp; /* Make proxy ARP entry for peer? */
bool neg_vj; /* Van Jacobson Compression? */
bool old_vj; /* use old (short) form of VJ option? */
Expand Down
11 changes: 10 additions & 1 deletion pppd/pppd.8
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ the gateway, when IPv6CP negotiation is successfully completed.
This entry is removed when the PPP connection is broken. This option
is privileged if the \fInodefaultroute6\fR option has been specified.
.TP
.B replacedefaultroute
This option is a flag to the defaultroute option. If defaultroute is
set and this flag is also set, pppd replaces an existing default route
with the new default route. This option is privileged.
.TP
.B disconnect \fIscript
Execute the command specified by \fIscript\fR, by passing it to a
shell, after
Expand Down Expand Up @@ -792,9 +797,13 @@ disable both forms of hardware flow control.
.TP
.B nodefaultroute
Disable the \fIdefaultroute\fR option. The system administrator who
wishes to prevent users from creating default routes with pppd
wishes to prevent users from adding a default route with pppd
can do so by placing this option in the /etc/ppp/options file.
.TP
.B noreplacedefaultroute
Disable the \fIreplacedefaultroute\fR option. This allows to disable a
\fIreplacedefaultroute\fR option set previously in the configuration.
.TP
.B nodefaultroute6
Disable the \fIdefaultroute6\fR option. The system administrator who
wishes to prevent users from adding a default route with pppd
Expand Down
2 changes: 1 addition & 1 deletion pppd/pppd.h
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@ int sif6addr(int, eui64_t, eui64_t);
int cif6addr(int, eui64_t, eui64_t);
/* Remove an IPv6 address from i/f */
#endif
int sifdefaultroute(int, u_int32_t, u_int32_t);
int sifdefaultroute(int, u_int32_t, u_int32_t, bool replace_default_rt);
/* Create default route through i/f */
int cifdefaultroute(int, u_int32_t, u_int32_t);
/* Delete default route through i/f */
Expand Down
74 changes: 64 additions & 10 deletions pppd/sys-linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ static int if_is_up; /* Interface has been marked up */
static int if6_is_up; /* Interface has been marked up for IPv6, to help differentiate */
static int have_default_route; /* Gateway for default route added */
static int have_default_route6; /* Gateway for default IPv6 route added */
static struct rtentry old_def_rt; /* Old default route */
static int default_rt_repl_rest; /* replace and restore old default rt */
static u_int32_t proxy_arp_addr; /* Addr for proxy arp entry added */
static char proxy_arp_dev[16]; /* Device for proxy arp entry */
static u_int32_t our_old_addr; /* for detecting address changes */
Expand Down Expand Up @@ -1573,6 +1575,9 @@ static int read_route_table(struct rtentry *rt)
p = NULL;
}

SET_SA_FAMILY (rt->rt_dst, AF_INET);
SET_SA_FAMILY (rt->rt_gateway, AF_INET);

SIN_ADDR(rt->rt_dst) = strtoul(cols[route_dest_col], NULL, 16);
SIN_ADDR(rt->rt_gateway) = strtoul(cols[route_gw_col], NULL, 16);
SIN_ADDR(rt->rt_genmask) = strtoul(cols[route_mask_col], NULL, 16);
Expand Down Expand Up @@ -1645,20 +1650,53 @@ int have_route_to(u_int32_t addr)
/********************************************************************
*
* sifdefaultroute - assign a default route through the address given.
*
* If the global default_rt_repl_rest flag is set, then this function
* already replaced the original system defaultroute with some other
* route and it should just replace the current defaultroute with
* another one, without saving the current route. Use: demand mode,
* when pppd sets first a defaultroute it it's temporary ppp0 addresses
* and then changes the temporary addresses to the addresses for the real
* ppp connection when it has come up.
*/

int sifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway)
int sifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway, bool replace)
{
struct rtentry rt;
struct rtentry rt, tmp_rt;
struct rtentry *del_rt = NULL;

if (defaultroute_exists(&rt, dfl_route_metric) && strcmp(rt.rt_dev, ifname) != 0) {
if (rt.rt_flags & RTF_GATEWAY)
error("not replacing existing default route via %I with metric %d",
SIN_ADDR(rt.rt_gateway), dfl_route_metric);
else
error("not replacing existing default route through %s with metric %d",
rt.rt_dev, dfl_route_metric);
return 0;
if (default_rt_repl_rest) {
/* We have already replaced the original defaultroute, if we
* are called again, we will delete the current default route
* and set the new default route in this function.
* - this is normally only the case the doing demand: */
if (defaultroute_exists(&tmp_rt, -1))
del_rt = &tmp_rt;
} else if (defaultroute_exists(&old_def_rt, -1 ) &&
strcmp( old_def_rt.rt_dev, ifname) != 0) {
/*
* We did not yet replace an existing default route, let's
* check if we should save and replace a default route:
*/
u_int32_t old_gateway = SIN_ADDR(old_def_rt.rt_gateway);

if (old_gateway != gateway) {
if (!replace) {
error("not replacing default route to %s [%I]",
old_def_rt.rt_dev, old_gateway);
return 0;
} else {
/* we need to copy rt_dev because we need it permanent too: */
char * tmp_dev = malloc(strlen(old_def_rt.rt_dev)+1);
strcpy(tmp_dev, old_def_rt.rt_dev);
old_def_rt.rt_dev = tmp_dev;

notice("replacing old default route to %s [%I]",
old_def_rt.rt_dev, old_gateway);
default_rt_repl_rest = 1;
del_rt = &old_def_rt;
}
}
}

memset (&rt, 0, sizeof (rt));
Expand All @@ -1678,6 +1716,12 @@ int sifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway)
error("default route ioctl(SIOCADDRT): %m");
return 0;
}
if (default_rt_repl_rest && del_rt)
if (ioctl(sock_fd, SIOCDELRT, del_rt) < 0) {
if ( ! ok_error ( errno ))
error("del old default route ioctl(SIOCDELRT): %m(%d)", errno);
return 0;
}

have_default_route = 1;
return 1;
Expand Down Expand Up @@ -1716,6 +1760,16 @@ int cifdefaultroute (int unit, u_int32_t ouraddr, u_int32_t gateway)
return 0;
}
}
if (default_rt_repl_rest) {
notice("restoring old default route to %s [%I]",
old_def_rt.rt_dev, SIN_ADDR(old_def_rt.rt_gateway));
if (ioctl(sock_fd, SIOCADDRT, &old_def_rt) < 0) {
if ( ! ok_error ( errno ))
error("restore default route ioctl(SIOCADDRT): %m(%d)", errno);
return 0;
}
default_rt_repl_rest = 0;
}

return 1;
}
Expand Down
7 changes: 6 additions & 1 deletion pppd/sys-solaris.c
Original file line number Diff line number Diff line change
Expand Up @@ -2038,10 +2038,15 @@ cifaddr(int u, u_int32_t o, u_int32_t h)
* sifdefaultroute - assign a default route through the address given.
*/
int
sifdefaultroute(int u, u_int32_t l, u_int32_t g)
sifdefaultroute(int u, u_int32_t l, u_int32_t g, bool replace)
{
struct rtentry rt;

if (replace) {
error("Replacing the default route is not implemented on Solaris yet");
return 0;
}

#if defined(__USLC__)
g = l; /* use the local address as gateway */
#endif
Expand Down

0 comments on commit d0ccb87

Please sign in to comment.