Allow src ipaddr to be set on a per client basis
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sat, 10 Nov 2012 18:49:40 +0000 (18:49 +0000)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Mon, 12 Nov 2012 13:42:18 +0000 (13:42 +0000)
share/dictionary.freeradius.internal
src/include/radius.h
src/include/radiusd.h
src/main/client.c
src/main/listen.c

index 11e41a9..5f54662 100644 (file)
@@ -178,6 +178,7 @@ ATTRIBUTE   Home-Server-Pool                        1111    string
 
 ATTRIBUTE      FreeRADIUS-Client-IP-Address            1120    ipaddr
 ATTRIBUTE      FreeRADIUS-Client-IPv6-Address          1121    ipv6addr
+
 ATTRIBUTE      FreeRADIUS-Client-Require-MA            1122    integer
 
 VALUE  FreeRADIUS-Client-Require-MA    no                      0
@@ -228,6 +229,11 @@ ATTRIBUTE  Cache-Entry-Hits                        1142    integer
 VALUE  Cache-Status-Only               no                      0
 VALUE  Cache-Status-Only               yes                     1
 
+#      More dynamic client attributes
+
+ATTRIBUTE      FreeRADIUS-Client-Src-IP-Address        1143    ipaddr
+ATTRIBUTE      FreeRADIUS-Client-Src-IPv6-Address      1144    ipv6addr
+
 #
 #      Range:  1200-1279
 #              EAP-SIM (and other EAP type) weirdness.
index 47cd171..9fe596e 100644 (file)
 #define PW_MODULE_RETURN_CODE          1108
 #define PW_PACKET_ORIGINAL_TIMESTAMP   1109
 #define PW_HOME_SERVER_POOL            1111
+#define PW_FREERADIUS_CLIENT_IP_ADDRESS                1120
+#define PW_FREERADIUS_CLIENT_IPV6_ADDRESS      1121
 #define PW_RECV_COA_TYPE               1131
 #define PW_SEND_COA_TYPE               1132
 #define PW_MSCHAP_PASSWORD             1133
 #define PW_CACHE_TTL                   1140
 #define PW_CACHE_STATUS_ONLY           1141
 #define PW_CACHE_ENTRY_HITS            1142
+#define PW_FREERADIUS_CLIENT_SRC_IP_ADDRESS    1143
+#define PW_FREERADIUS_CLIENT_SRC_IPV6_ADDRESS  1144
 
 /*
  *     Integer Translations
index 4dea3fa..b313336 100644 (file)
@@ -74,6 +74,7 @@ typedef struct request_data_t request_data_t;
 
 typedef struct radclient {
        fr_ipaddr_t             ipaddr;
+       fr_ipaddr_t             src_ipaddr;
        int                     prefix;
        char                    *longname;
        char                    *secret;
index e9864d5..0e08f3b 100644 (file)
@@ -500,6 +500,7 @@ RADCLIENT *client_find_old(const fr_ipaddr_t *ipaddr)
 
 static struct in_addr cl_ip4addr;
 static struct in6_addr cl_ip6addr;
+static char *cl_srcipaddr = NULL;
 #ifdef WITH_TCP
 static char *hs_proto = NULL;
 #endif
@@ -527,6 +528,9 @@ static const CONF_PARSER client_config[] = {
        { "netmask",  PW_TYPE_INTEGER,
          offsetof(RADCLIENT, prefix), 0, NULL },
 
+       { "src_ipaddr",  PW_TYPE_STRING_PTR,
+         0, &cl_srcipaddr,  NULL },
+         
        { "require_message_authenticator",  PW_TYPE_BOOLEAN,
          offsetof(RADCLIENT, message_authenticator), 0, "no" },
 
@@ -601,6 +605,8 @@ static RADCLIENT *client_parse(CONF_SECTION *cs, int in_server)
 #ifdef WITH_TCP
                free(hs_proto);
                hs_proto = NULL;
+               free(cl_srcipaddr);
+               cl_srcipaddr = NULL;
 #endif
 
                return NULL;
@@ -721,6 +727,25 @@ static RADCLIENT *client_parse(CONF_SECTION *cs, int in_server)
 #endif
        }
 
+       /*
+        *      If a src_ipaddr is specified, when we send the return packet
+        *      we will use this address instead of the src from the
+        *      request.
+        */
+       if (cl_srcipaddr) {
+#ifdef WITH_UDPFROMTO
+               if (ip_hton(cl_srcipaddr, c->ipaddr.af, &c->src_ipaddr) < 0) {
+                       cf_log_err(cf_sectiontoitem(cs), "Failed parsing src_ipaddr");
+                       goto error;
+               }
+#else
+               DEBUG("WARNING: Server not build with udpfromto, ignoring client src_ipaddr");
+#endif
+               
+               free(cl_srcipaddr);
+               cl_srcipaddr = NULL;
+       }
+       
        if (c->prefix < 0) switch (c->ipaddr.af) {
        case AF_INET:
                c->prefix = 32;
@@ -970,6 +995,10 @@ static const CONF_PARSER dynamic_config[] = {
          offsetof(RADCLIENT, ipaddr), 0, NULL },
        { "FreeRADIUS-Client-IPv6-Address",  PW_TYPE_IPV6ADDR,
          offsetof(RADCLIENT, ipaddr), 0, NULL },
+       { "FreeRADIUS-Client-Src-IP-Address",  PW_TYPE_IPADDR,
+         offsetof(RADCLIENT, src_ipaddr), 0, NULL },
+       { "FreeRADIUS-Client-Src-IPv6-Address",  PW_TYPE_IPV6ADDR,
+         offsetof(RADCLIENT, src_ipaddr), 0, NULL },
 
        { "FreeRADIUS-Client-Require-MA",  PW_TYPE_BOOLEAN,
          offsetof(RADCLIENT, message_authenticator), NULL, NULL },
@@ -1055,7 +1084,8 @@ RADCLIENT *client_create(RADCLIENT_LIST *clients, REQUEST *request)
        memset(c, 0, sizeof(*c));
        c->cs = request->client->cs;
        c->ipaddr.af = AF_UNSPEC;
-
+       c->src_ipaddr.af = AF_UNSPEC;
+       
        for (i = 0; dynamic_config[i].name != NULL; i++) {
                DICT_ATTR *da;
                VALUE_PAIR *vp;
@@ -1087,15 +1117,35 @@ RADCLIENT *client_create(RADCLIENT_LIST *clients, REQUEST *request)
 
                switch (dynamic_config[i].type) {
                case PW_TYPE_IPADDR:
-                       c->ipaddr.af = AF_INET;
-                       c->ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
-                       c->prefix = 32;
+                       if (da->type == PW_FREERADIUS_CLIENT_IP_ADDRESS) {
+                               c->ipaddr.af = AF_INET;
+                               c->ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+                               c->prefix = 32;
+                       } else if (da->type == PW_FREERADIUS_CLIENT_SRC_IP_ADDRESS) {
+#ifdef WITH_UDPFROMTO
+                               c->src_ipaddr.af = AF_INET;
+                               c->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+#else
+                               DEBUG("WARNING: Server not build with udpfromto, ignoring FreeRADIUS-Client-Src-IP-Address.");
+#endif
+                       }
+                       
                        break;
 
                case PW_TYPE_IPV6ADDR:
-                       c->ipaddr.af = AF_INET6;
-                       c->ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
-                       c->prefix = 128;
+                       if (da->type == PW_FREERADIUS_CLIENT_SRC_IPV6_ADDRESS) {
+                               c->ipaddr.af = AF_INET6;
+                               c->ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
+                               c->prefix = 128;
+                       } else if (da->type == PW_FREERADIUS_CLIENT_SRC_IPV6_ADDRESS) {
+#ifdef WITH_UDPFROMTO
+                               c->src_ipaddr.af = AF_INET6;
+                               c->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
+#else
+                               DEBUG("WARNING: Server not build with udpfromto, ignoring FreeRADIUS-Client-Src-IPv6-Address.");
+#endif
+                       }
+                       
                        break;
 
                case PW_TYPE_STRING_PTR:
@@ -1147,6 +1197,14 @@ RADCLIENT *client_create(RADCLIENT_LIST *clients, REQUEST *request)
        if (!client_validate(clients, request->client, c)) {
                return NULL;
        }
+       
+       if ((c->src_ipaddr.af != AF_UNSPEC) && (c->src_ipaddr.af != c->ipaddr.af)) {
+               DEBUG("- Cannot add client %s: Client IP and src address are different IP version.",
+                     ip_ntoh(&request->packet->src_ipaddr,
+                             buffer, sizeof(buffer)));
+               
+               goto error;
+       }
 
        return c;
 }
index c0e1bcd..5b1cb37 100644 (file)
@@ -1240,6 +1240,18 @@ static int auth_socket_send(rad_listen_t *listener, REQUEST *request)
        rad_assert(request->listener == listener);
        rad_assert(listener->send == auth_socket_send);
 
+#ifdef WITH_UDPFROMTO
+       /*
+        *      Overwrite the src ip address on the outbound packet
+        *      with the one specified by the client.
+        *      This is useful to work around broken DSR implementations
+        *      and other routing issues.
+        */
+       if (request->client->src_ipaddr.af != AF_UNSPEC) {
+               request->reply->src_ipaddr = request->client->src_ipaddr;
+       }
+#endif
+       
        if (rad_send(request->reply, request->packet,
                     request->client->secret) < 0) {
                radlog_request(L_ERR, 0, request, "Failed sending reply: %s",
@@ -1268,6 +1280,18 @@ static int acct_socket_send(rad_listen_t *listener, REQUEST *request)
         */
        if (request->reply->code == 0) return 0;
 
+#ifdef WITH_UDPFROMTO
+       /*
+        *      Overwrite the src ip address on the outbound packet
+        *      with the one specified by the client.
+        *      This is useful to work around broken DSR implementations
+        *      and other routing issues.
+        */
+       if (request->client->src_ipaddr.af != AF_UNSPEC) {
+               request->reply->src_ipaddr = request->client->src_ipaddr;
+       }
+#endif
+       
        if (rad_send(request->reply, request->packet,
                     request->client->secret) < 0) {
                radlog_request(L_ERR, 0, request, "Failed sending reply: %s",