device/ip-tunnel: add support for IP6TNL tunnels
authorBeniamino Galvani <bgalvani@redhat.com>
Mon, 30 Nov 2015 20:22:13 +0000 (21:22 +0100)
committerBeniamino Galvani <bgalvani@redhat.com>
Tue, 1 Dec 2015 16:39:41 +0000 (17:39 +0100)
introspection/nm-device-ip-tunnel.xml
src/devices/nm-device-ip-tunnel.c
src/devices/nm-device-ip-tunnel.h

index db5335b..fcb42ce 100644 (file)
       </tp:docstring>
     </property>
 
+    <property name="EncapsulationLimit" type="y" access="read">
+      <tp:docstring>
+        How many additional levels of encapsulation are permitted to
+        be prepended to packets. This property applies only to IPv6
+        tunnels.
+      </tp:docstring>
+    </property>
+
+    <property name="FlowLabel" type="u" access="read">
+      <tp:docstring>
+        The flow label to assign to tunnel packets. This property
+        applies only to IPv6 tunnels.
+      </tp:docstring>
+    </property>
+
     <signal name="PropertiesChanged">
         <arg name="properties" type="a{sv}" tp:type="String_Variant_Map">
             <tp:docstring>
index faaf214..746fb17 100644 (file)
@@ -57,6 +57,8 @@ typedef struct {
        int addr_family;
        char *input_key;
        char *output_key;
+       guint8 encap_limit;
+       guint32 flow_label;
 } NMDeviceIPTunnelPrivate;
 
 enum {
@@ -70,6 +72,8 @@ enum {
        PROP_PATH_MTU_DISCOVERY,
        PROP_INPUT_KEY,
        PROP_OUTPUT_KEY,
+       PROP_ENCAPSULATION_LIMIT,
+       PROP_FLOW_LABEL,
 
        LAST_PROP
 };
@@ -118,8 +122,9 @@ update_properties (NMDevice *device)
        int parent_ifindex;
        in_addr_t local4, remote4;
        struct in6_addr local6, remote6;
-       guint8 ttl, tos;
-       gboolean pmtud;
+       guint8 ttl = 0, tos = 0, encap_limit = 0;
+       gboolean pmtud = FALSE;
+       guint32 flow_label = 0;
        char *key;
 
        if (priv->mode == NM_IP_TUNNEL_MODE_GRE) {
@@ -197,6 +202,23 @@ update_properties (NMDevice *device)
                ttl = lnk->ttl;
                tos = lnk->tos;
                pmtud = lnk->path_mtu_discovery;
+       } else if (   priv->mode == NM_IP_TUNNEL_MODE_IPIP6
+                  || priv->mode == NM_IP_TUNNEL_MODE_IP6IP6) {
+               const NMPlatformLnkIp6Tnl *lnk;
+
+               lnk = nm_platform_link_get_lnk_ip6tnl (NM_PLATFORM_GET, nm_device_get_ifindex (device), NULL);
+               if (!lnk) {
+                       _LOGW (LOGD_HW, "could not read %s properties", "ip6tnl");
+                       return;
+               }
+
+               parent_ifindex = lnk->parent_ifindex;
+               local6 = lnk->local;
+               remote6 = lnk->remote;
+               ttl = lnk->ttl;
+               tos = lnk->tclass;
+               encap_limit = lnk->encap_limit;
+               flow_label = lnk->flow_label;
        } else
                g_return_if_reached ();
 
@@ -253,6 +275,16 @@ update_properties (NMDevice *device)
                priv->path_mtu_discovery = pmtud;
                g_object_notify (object, NM_DEVICE_IP_TUNNEL_PATH_MTU_DISCOVERY);
        }
+
+       if (priv->encap_limit != encap_limit) {
+               priv->encap_limit = encap_limit;
+               g_object_notify (object, NM_DEVICE_IP_TUNNEL_ENCAPSULATION_LIMIT);
+       }
+
+       if (priv->flow_label != flow_label) {
+               priv->flow_label = flow_label;
+               g_object_notify (object, NM_DEVICE_IP_TUNNEL_FLOW_LABEL);
+       }
 }
 
 static void
@@ -352,6 +384,20 @@ update_connection (NMDevice *device, NMConnection *connection)
                              NULL);
        }
 
+       if (nm_setting_ip_tunnel_get_encapsulation_limit (s_ip_tunnel) != priv->encap_limit) {
+               g_object_set (G_OBJECT (s_ip_tunnel),
+                                       NM_SETTING_IP_TUNNEL_ENCAPSULATION_LIMIT,
+                                       priv->encap_limit,
+                                       NULL);
+       }
+
+       if (nm_setting_ip_tunnel_get_flow_label (s_ip_tunnel) != priv->flow_label) {
+               g_object_set (G_OBJECT (s_ip_tunnel),
+                                       NM_SETTING_IP_TUNNEL_FLOW_LABEL,
+                                       priv->flow_label,
+                                       NULL);
+       }
+
        if (priv->mode == NM_IP_TUNNEL_MODE_GRE || priv->mode == NM_IP_TUNNEL_MODE_IP6GRE) {
                if (g_strcmp0 (nm_setting_ip_tunnel_get_input_key (s_ip_tunnel), priv->input_key)) {
                        g_object_set (G_OBJECT (s_ip_tunnel),
@@ -452,8 +498,16 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
        if (nm_setting_ip_tunnel_get_tos (s_ip_tunnel) != priv->tos)
                return FALSE;
 
-       if (nm_setting_ip_tunnel_get_path_mtu_discovery (s_ip_tunnel) != priv->path_mtu_discovery)
-               return FALSE;
+       if (priv->addr_family == AF_INET) {
+               if (nm_setting_ip_tunnel_get_path_mtu_discovery (s_ip_tunnel) != priv->path_mtu_discovery)
+                       return FALSE;
+       } else {
+               if (nm_setting_ip_tunnel_get_encapsulation_limit (s_ip_tunnel) != priv->encap_limit)
+                       return FALSE;
+
+               if (nm_setting_ip_tunnel_get_flow_label (s_ip_tunnel) != priv->flow_label)
+                       return FALSE;
+       }
 
        return TRUE;
 }
@@ -461,9 +515,19 @@ check_connection_compatible (NMDevice *device, NMConnection *connection)
 static NMIPTunnelMode
 platform_link_to_tunnel_mode (const NMPlatformLink *link)
 {
+       const NMPlatformLnkIp6Tnl *lnk;
+
        switch (link->type) {
        case NM_LINK_TYPE_GRE:
                return NM_IP_TUNNEL_MODE_GRE;
+       case NM_LINK_TYPE_IP6TNL:
+               lnk = nm_platform_link_get_lnk_ip6tnl (NM_PLATFORM_GET, link->ifindex, NULL);
+               if (lnk->proto == IPPROTO_IPIP)
+                       return NM_IP_TUNNEL_MODE_IPIP6;
+               else if (lnk->proto == IPPROTO_IPV6)
+                       return NM_IP_TUNNEL_MODE_IP6IP6;
+               else
+                       return NM_IP_TUNNEL_MODE_UKNOWN;
        case NM_LINK_TYPE_IPIP:
                return NM_IP_TUNNEL_MODE_IPIP;
        case NM_LINK_TYPE_SIT:
@@ -485,7 +549,11 @@ constructed (GObject *object)
 {
        NMDeviceIPTunnelPrivate *priv = NM_DEVICE_IP_TUNNEL_GET_PRIVATE (object);
 
-       priv->addr_family = AF_INET; /* at the moment we support only IPv4 tunnels */
+       if (   priv->mode == NM_IP_TUNNEL_MODE_IPIP6
+           || priv->mode == NM_IP_TUNNEL_MODE_IP6IP6)
+               priv->addr_family = AF_INET6;
+       else
+               priv->addr_family = AF_INET;
 
        G_OBJECT_CLASS (nm_device_ip_tunnel_parent_class)->constructed (object);
 }
@@ -503,6 +571,7 @@ create_and_realize (NMDevice *device,
        NMPlatformLnkGre lnk_gre = { };
        NMPlatformLnkSit lnk_sit = { };
        NMPlatformLnkIpIp lnk_ipip = { };
+       NMPlatformLnkIp6Tnl lnk_ip6tnl = { };
        const char *str;
        gint64 val;
 
@@ -609,6 +678,35 @@ create_and_realize (NMDevice *device,
                        return FALSE;
                }
                break;
+       case NM_IP_TUNNEL_MODE_IPIP6:
+       case NM_IP_TUNNEL_MODE_IP6IP6:
+               if (parent)
+                       lnk_ip6tnl.parent_ifindex = nm_device_get_ifindex (parent);
+
+               str = nm_setting_ip_tunnel_get_local (s_ip_tunnel);
+               if (str)
+                       inet_pton (AF_INET6, str, &lnk_ip6tnl.local);
+
+               str = nm_setting_ip_tunnel_get_remote (s_ip_tunnel);
+               g_assert (str);
+               inet_pton (AF_INET6, str, &lnk_ip6tnl.remote);
+
+               lnk_ip6tnl.ttl = nm_setting_ip_tunnel_get_ttl (s_ip_tunnel);
+               lnk_ip6tnl.tclass = nm_setting_ip_tunnel_get_tos (s_ip_tunnel);
+               lnk_ip6tnl.encap_limit = nm_setting_ip_tunnel_get_encapsulation_limit (s_ip_tunnel);
+               lnk_ip6tnl.flow_label = nm_setting_ip_tunnel_get_flow_label (s_ip_tunnel);
+               lnk_ip6tnl.proto = nm_setting_ip_tunnel_get_mode (s_ip_tunnel) == NM_IP_TUNNEL_MODE_IPIP6 ? IPPROTO_IPIP : IPPROTO_IPV6;
+
+               plerr = nm_platform_link_ip6tnl_add (NM_PLATFORM_GET, iface, &lnk_ip6tnl, out_plink);
+               if (plerr != NM_PLATFORM_ERROR_SUCCESS && plerr != NM_PLATFORM_ERROR_EXISTS) {
+                       g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
+                                    "Failed to create IPIP interface '%s' for '%s': %s",
+                                    iface,
+                                    nm_connection_get_id (connection),
+                                    nm_platform_error_to_string (plerr));
+                       return FALSE;
+               }
+               break;
        default:
                g_set_error (error, NM_DEVICE_ERROR, NM_DEVICE_ERROR_CREATION_FAILED,
                             "Failed to create IP tunnel interface '%s' for '%s': mode %d not supported",
@@ -665,6 +763,12 @@ get_property (GObject *object, guint prop_id,
        case PROP_OUTPUT_KEY:
                g_value_set_string (value, priv->output_key);
                break;
+       case PROP_ENCAPSULATION_LIMIT:
+               g_value_set_uchar (value, priv->encap_limit);
+               break;
+       case PROP_FLOW_LABEL:
+               g_value_set_uint (value, priv->flow_label);
+               break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
@@ -774,6 +878,20 @@ nm_device_ip_tunnel_class_init (NMDeviceIPTunnelClass *klass)
                                      G_PARAM_READABLE |
                                      G_PARAM_STATIC_STRINGS));
 
+       g_object_class_install_property
+               (object_class, PROP_ENCAPSULATION_LIMIT,
+                 g_param_spec_uchar (NM_DEVICE_IP_TUNNEL_ENCAPSULATION_LIMIT, "", "",
+                                     0, 255, 0,
+                                     G_PARAM_READABLE |
+                                     G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property
+               (object_class, PROP_FLOW_LABEL,
+                g_param_spec_uint (NM_DEVICE_IP_TUNNEL_FLOW_LABEL, "", "",
+                                   0, (1 << 20) - 1, 0,
+                                   G_PARAM_READABLE |
+                                   G_PARAM_STATIC_STRINGS));
+
        nm_exported_object_class_add_interface (NM_EXPORTED_OBJECT_CLASS (klass),
                                                NMDBUS_TYPE_DEVICE_IPTUNNEL_SKELETON,
                                                NULL);
@@ -799,6 +917,9 @@ create_device (NMDeviceFactory *factory,
        } else
                mode = platform_link_to_tunnel_mode (plink);
 
+       if (mode == NM_IP_TUNNEL_MODE_UKNOWN)
+               return NULL;
+
        return (NMDevice *) g_object_new (NM_TYPE_DEVICE_IP_TUNNEL,
                                          NM_DEVICE_IFACE, iface,
                                          NM_DEVICE_TYPE_DESC, "IPTunnel",
index fdd92f3..11cb5bc 100644 (file)
@@ -42,6 +42,8 @@ G_BEGIN_DECLS
 #define NM_DEVICE_IP_TUNNEL_PATH_MTU_DISCOVERY  "path-mtu-discovery"
 #define NM_DEVICE_IP_TUNNEL_INPUT_KEY           "input-key"
 #define NM_DEVICE_IP_TUNNEL_OUTPUT_KEY          "output-key"
+#define NM_DEVICE_IP_TUNNEL_ENCAPSULATION_LIMIT "encapsulation-limit"
+#define NM_DEVICE_IP_TUNNEL_FLOW_LABEL          "flow-label"
 
 typedef struct {
        NMDevice parent;