aboutsummaryrefslogtreecommitdiffstats
path: root/vicn/resource/vpp
diff options
context:
space:
mode:
Diffstat (limited to 'vicn/resource/vpp')
-rw-r--r--vicn/resource/vpp/__init__.py0
-rw-r--r--vicn/resource/vpp/cicn.py138
-rw-r--r--vicn/resource/vpp/dpdk_device.py35
-rw-r--r--vicn/resource/vpp/interface.py125
-rw-r--r--vicn/resource/vpp/scripts.py287
-rw-r--r--vicn/resource/vpp/vpp.py187
-rw-r--r--vicn/resource/vpp/vpp_bridge.py130
-rw-r--r--vicn/resource/vpp/vpp_commands.py41
-rw-r--r--vicn/resource/vpp/vpp_host.py144
9 files changed, 1087 insertions, 0 deletions
diff --git a/vicn/resource/vpp/__init__.py b/vicn/resource/vpp/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/vicn/resource/vpp/__init__.py
diff --git a/vicn/resource/vpp/cicn.py b/vicn/resource/vpp/cicn.py
new file mode 100644
index 00000000..be523a6c
--- /dev/null
+++ b/vicn/resource/vpp/cicn.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017 Cisco and/or its affiliates.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import re
+
+from netmodel.model.type import Integer, Bool
+from vicn.core.attribute import Attribute
+from vicn.core.exception import ResourceNotFound
+from vicn.core.requirement import Requirement
+from vicn.core.resource_mgr import wait_resource_task
+from vicn.core.task import async_task, task, BashTask, EmptyTask
+from vicn.resource.icn.forwarder import Forwarder
+from vicn.resource.node import Node
+from vicn.resource.vpp.vpp_commands import CMD_VPP_ENABLE_PLUGIN
+from vicn.resource.vpp.vpp_commands import CMD_VPP_CICN_GET
+from vicn.resource.vpp.vpp_commands import CMD_VPP_ADD_ICN_FACE
+from vicn.resource.vpp.vpp_commands import CMD_VPP_ADD_ICN_ROUTE
+from vicn.resource.vpp.vpp_commands import CMD_VPP_CICN_GET_CACHE_SIZE
+from vicn.resource.vpp.vpp_commands import CMD_VPP_CICN_SET_CACHE_SIZE
+
+_ADD_FACE_RETURN_FORMAT = "Face ID: [0-9]+"
+
+def check_face_id_return_format(data):
+ prog = re.compile(_ADD_FACE_RETURN_FORMAT)
+ return prog.match(data)
+
+def parse_face_id(data):
+ return data.partition(':')[2]
+
+class CICNForwarder(Forwarder):
+ """
+ NOTE: Based on the Vagrantfile, we recommend a node with mem=4096, cpu=4
+ """
+
+ node = Attribute(Node,
+ mandatory=True,
+ requirements = [Requirement('vpp')],
+ reverse_name='cicn')
+ numa_node = Attribute(Integer,
+ description = 'Numa node on which vpp will run',
+ default = None)
+ core = Attribute(Integer,
+ description = 'Core belonging the numa node on which vpp will run',
+ default = None)
+ enable_worker = Attribute(Bool,
+ description = 'Enable one worker for packet processing',
+ default = False)
+
+ #__packages__ = ['vpp-plugin-cicn']
+
+ def __after__(self):
+ return ['CentralICN']
+
+ def __get__(self):
+ def parse(rv):
+ if rv.return_value > 0 or 'cicn: not enabled' in rv.stdout:
+ raise ResourceNotFound
+ return BashTask(self.node, CMD_VPP_CICN_GET,
+ lock = self.node.vpp.vppctl_lock, parse=parse)
+
+ def __create__(self):
+
+ #self.node.vpp.plugins.append("cicn")
+ lock = self.node.vpp.vppctl_lock
+ create_task = BashTask(self.node, CMD_VPP_ENABLE_PLUGIN,
+ {'plugin' : 'cicn'}, lock = lock)
+
+ face_task = EmptyTask()
+ route_task = EmptyTask()
+
+ def parse_face(rv, face):
+ if check_face_id_return_format(rv.stdout):
+ face.id = parse_face_id(rv.stdout)
+ return {}
+
+ for face in self.faces:
+ face_task = face_task > BashTask(self.node, CMD_VPP_ADD_ICN_FACE,
+ {'face':face},
+ parse = (lambda x : parse_face(x, face)), lock = lock)
+
+ if not self.routes:
+ from vicn.resource.icn.route import Route
+ for route in self._state.manager.by_type(Route):
+ if route.node is self.node:
+ self.routes.append(route)
+ for route in self.routes:
+ route_task = route_task > BashTask(self.node,
+ CMD_VPP_ADD_ICN_ROUTE, {'route' : route}, lock = lock)
+
+ return (wait_resource_task(self.node.vpp) > create_task) > (face_task > route_task)
+
+ # Nothing to do
+ __delete__ = None
+
+ #--------------------------------------------------------------------------
+ # Attributes
+ #--------------------------------------------------------------------------
+
+ # Force local update
+
+ _add_faces = None
+ _remove_faces = None
+ _get_faces = None
+ _set_faces = None
+
+ _add_routes = None
+ _remove_routes = None
+ _get_routes = None
+ _set_routes = None
+
+ #--------------------------------------------------------------------------
+ # Internal methods
+ #--------------------------------------------------------------------------
+
+ def _set_cache_size(self):
+ return BashTask(self.node, CMD_VPP_CICN_SET_CACHE_SIZE, {'self': self},
+ lock = self.node.vpp.vppctl_lock)
+
+ def _get_cache_size(self):
+ def parse(rv):
+ return int(rv.stdout)
+ return BashTask(self.node, CMD_VPP_CICN_GET_CACHE_SIZE, parse=parse,
+ lock = self.node.vpp.vppctl_lock)
diff --git a/vicn/resource/vpp/dpdk_device.py b/vicn/resource/vpp/dpdk_device.py
new file mode 100644
index 00000000..69449e48
--- /dev/null
+++ b/vicn/resource/vpp/dpdk_device.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017 Cisco and/or its affiliates.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+from netmodel.model.type import Integer, String
+from vicn.core.attribute import Attribute
+from vicn.resource.linux.phy_interface import PhyInterface
+
+class DpdkDevice(PhyInterface):
+ """
+ Resource: DpdkDevice
+
+ A DpdkDevice is a physical net device supported by Dpdk and with parameters
+ specific to VPP.
+ """
+ numa_node = Attribute(Integer,
+ description = 'NUMA node on the same PCI bus as the DPDK card')
+ socket_mem = Attribute(Integer,
+ description = 'Memory used by the vpp forwarder',
+ default = 512)
+ mac_address = Attribute(String)
diff --git a/vicn/resource/vpp/interface.py b/vicn/resource/vpp/interface.py
new file mode 100644
index 00000000..efe4fe5a
--- /dev/null
+++ b/vicn/resource/vpp/interface.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017 Cisco and/or its affiliates.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+from netmodel.model.type import Integer, String, Bool
+from vicn.core.resource import Resource
+from vicn.core.attribute import Attribute, Multiplicity
+from vicn.core.exception import ResourceNotFound
+from vicn.core.task import inline_task, BashTask, task
+from vicn.core.task import EmptyTask
+from vicn.resource.interface import Interface
+from vicn.resource.linux.net_device import NonTapBaseNetDevice
+from vicn.resource.vpp.vpp import VPP
+from vicn.resource.vpp.vpp_commands import CMD_VPP_CREATE_IFACE
+from vicn.resource.vpp.vpp_commands import CMD_VPP_SET_IP, CMD_VPP_SET_UP
+
+class VPPInterface(Resource):
+ """
+ Resource: VPPInterface
+
+ An interface representation in VPP
+ """
+
+ vpp = Attribute(VPP,
+ description = 'Forwarder to which this interface belong to',
+ mandatory = True,
+ multiplicity = Multiplicity.ManyToOne,
+ key = True,
+ reverse_name = 'interfaces')
+ parent = Attribute(Interface, description = 'parent',
+ mandatory = True, reverse_name = 'vppinterface')
+ ip_address = Attribute(String)
+ prefix_len = Attribute(Integer, default = 31)
+ up = Attribute(Bool, description = 'Interface up/down status')
+ monitored = Attribute(Bool, default = True)
+
+ device_name = Attribute(String)
+
+ #--------------------------------------------------------------------------
+ # Resource lifecycle
+ #--------------------------------------------------------------------------
+
+ def __after__(self):
+ """
+ We need CentralIP to get the parent interface IP address
+ """
+ return ['CentralIP']
+
+ @inline_task
+ def __get__(self):
+ raise ResourceNotFound
+
+ def __create__(self):
+ # We must control what is the type of the parent netDevice (currently
+ # supported only veths, physical nics are coming)
+ create_task = EmptyTask()
+
+ # We must let the routing algorithm know that the parent interface
+ # belongs to vpp
+ self.parent.has_vpp_child = True
+
+ self.ip_address = self.parent.ip_address
+ self.up = True
+
+ if isinstance(self.parent,NonTapBaseNetDevice):
+ # Remove ip address in the parent device, it must only be set in
+ # the vpp interface otherwise vpp and the linux kernel will reply
+ # to non-icn request (e.g., ARP replies, port ureachable etc)
+
+ self.device_name = 'host-' + self.parent.device_name
+ create_task = BashTask(self.vpp.node, CMD_VPP_CREATE_IFACE,
+ {'vpp_interface': self},
+ lock = self.vpp.vppctl_lock)
+
+ self.parent.set('ip_address', None)
+ self.parent.set('offload', False)
+ self.parent.remote.set('offload', False)
+
+ elif self.parent.get_type() == 'dpdkdevice':
+ self.device_name = self.parent.device_name
+ else :
+ # Currently assume naively that everything else will be a physical
+ # NIC for VPP
+ #
+ # Before initialization, we need to make sure that the parent
+ # interface is down (vpp will control the nic)
+ self.device_name = 'host-' + self.parent.device_name
+ self.parent.set('up', False)
+
+ return create_task
+
+ #--------------------------------------------------------------------------
+ # Attributes
+ #--------------------------------------------------------------------------
+
+ def _set_ip_address(self):
+ if self.ip_address:
+ return BashTask(self.vpp.node, CMD_VPP_SET_IP, {'netdevice': self},
+ lock = self.vpp.vppctl_lock)
+
+ def _set_up(self):
+ return BashTask(self.vpp.node, CMD_VPP_SET_UP, {'netdevice': self},
+ lock = self.vpp.vppctl_lock)
+
+ @task
+ def _get_up(self):
+ return {'up' : False}
+
+ @task
+ def _get_ip_address(self):
+ return {'ip_address' : None}
diff --git a/vicn/resource/vpp/scripts.py b/vicn/resource/vpp/scripts.py
new file mode 100644
index 00000000..3a3d5e8f
--- /dev/null
+++ b/vicn/resource/vpp/scripts.py
@@ -0,0 +1,287 @@
+FN_APPARMOR_DPDK_SCRIPT='/etc/apparmor.d/lxc/lxc-dpdk'
+
+TPL_APPARMOR_DPDK_SCRIPT='''
+profile lxc-dpdk flags=(attach_disconnected,mediate_deleted) {
+ ### Base profile
+ capability,
+ dbus,
+ file,
+ network,
+ umount,
+
+ # Allow us to receive signals from anywhere.
+ signal (receive),
+
+ # Allow us to send signals to ourselves
+ signal peer=@{profile_name},
+
+ # Allow other processes to read our /proc entries, futexes, perf tracing and
+ # kcmp for now (they will need 'read' in the first place). Administrators can
+ # override with:
+ # deny ptrace (readby) ...
+ ptrace (readby),
+
+ # Allow other processes to trace us by default (they will need 'trace' in
+ # the first place). Administrators can override with:
+ # deny ptrace (tracedby) ...
+ ptrace (tracedby),
+
+ # Allow us to ptrace ourselves
+ ptrace peer=@{profile_name},
+
+ # ignore DENIED message on / remount
+ deny mount options=(ro, remount) -> /,
+ deny mount options=(ro, remount, silent) -> /,
+
+ # allow tmpfs mounts everywhere
+ mount fstype=tmpfs,
+
+ # allow hugetlbfs mounts everywhere
+ mount fstype=hugetlbfs,
+
+ # allow mqueue mounts everywhere
+ mount fstype=mqueue,
+
+ # allow fuse mounts everywhere
+ mount fstype=fuse,
+ mount fstype=fuse.*,
+
+ # deny access under /proc/bus to avoid e.g. messing with pci devices directly
+ deny @{PROC}/bus/** wklx,
+
+ # deny writes in /proc/sys/fs but allow binfmt_misc to be mounted
+ mount fstype=binfmt_misc -> /proc/sys/fs/binfmt_misc/,
+ deny @{PROC}/sys/fs/** wklx,
+
+ # allow efivars to be mounted, writing to it will be blocked though
+ mount fstype=efivarfs -> /sys/firmware/efi/efivars/,
+
+ # block some other dangerous paths
+ deny @{PROC}/kcore rwklx,
+ deny @{PROC}/kmem rwklx,
+ deny @{PROC}/mem rwklx,
+ deny @{PROC}/sysrq-trigger rwklx,
+
+ # deny writes in /sys except for /sys/fs/cgroup, also allow
+ # fusectl, securityfs and debugfs to be mounted there (read-only)
+ mount fstype=fusectl -> /sys/fs/fuse/connections/,
+ mount fstype=securityfs -> /sys/kernel/security/,
+ mount fstype=debugfs -> /sys/kernel/debug/,
+ deny mount fstype=debugfs -> /var/lib/ureadahead/debugfs/,
+ mount fstype=proc -> /proc/,
+ mount fstype=sysfs -> /sys/,
+ mount options=(rw, nosuid, nodev, noexec, remount) -> /sys/,
+ deny /sys/firmware/efi/efivars/** rwklx,
+ # note, /sys/kernel/security/** handled below
+ mount options=(move) /sys/fs/cgroup/cgmanager/ -> /sys/fs/cgroup/cgmanager.lower/,
+ mount options=(ro, nosuid, nodev, noexec, remount, strictatime) -> /sys/fs/cgroup/,
+
+ mount options=(ro, nosuid, nodev, noexec, remount, strictatime) -> /sys,
+
+ # deny reads from debugfs
+ deny /sys/kernel/debug/{,**} rwklx,
+
+ # allow paths to be made slave, shared, private or unbindable
+ # FIXME: This currently doesn't work due to the apparmor parser treating those as allowing all mounts.
+# mount options=(rw,make-slave) -> **,
+# mount options=(rw,make-rslave) -> **,
+# mount options=(rw,make-shared) -> **,
+# mount options=(rw,make-rshared) -> **,
+# mount options=(rw,make-private) -> **,
+# mount options=(rw,make-rprivate) -> **,
+# mount options=(rw,make-unbindable) -> **,
+# mount options=(rw,make-runbindable) -> **,
+
+ # allow bind-mounts of anything except /proc, /sys and /dev
+ mount options=(rw,bind) /[^spd]*{,/**},
+ mount options=(rw,bind) /d[^e]*{,/**},
+ mount options=(rw,bind) /de[^v]*{,/**},
+ mount options=(rw,bind) /dev/.[^l]*{,/**},
+ mount options=(rw,bind) /dev/.l[^x]*{,/**},
+ mount options=(rw,bind) /dev/.lx[^c]*{,/**},
+ mount options=(rw,bind) /dev/.lxc?*{,/**},
+ mount options=(rw,bind) /dev/[^.]*{,/**},
+ mount options=(rw,bind) /dev?*{,/**},
+ mount options=(rw,bind) /p[^r]*{,/**},
+ mount options=(rw,bind) /pr[^o]*{,/**},
+ mount options=(rw,bind) /pro[^c]*{,/**},
+ mount options=(rw,bind) /proc?*{,/**},
+ mount options=(rw,bind) /s[^y]*{,/**},
+ mount options=(rw,bind) /sy[^s]*{,/**},
+ mount options=(rw,bind) /sys?*{,/**},
+
+ # allow moving mounts except for /proc, /sys and /dev
+ mount options=(rw,move) /[^spd]*{,/**},
+ mount options=(rw,move) /d[^e]*{,/**},
+ mount options=(rw,move) /de[^v]*{,/**},
+ mount options=(rw,move) /dev/.[^l]*{,/**},
+ mount options=(rw,move) /dev/.l[^x]*{,/**},
+ mount options=(rw,move) /dev/.lx[^c]*{,/**},
+ mount options=(rw,move) /dev/.lxc?*{,/**},
+ mount options=(rw,move) /dev/[^.]*{,/**},
+ mount options=(rw,move) /dev?*{,/**},
+ mount options=(rw,move) /p[^r]*{,/**},
+ mount options=(rw,move) /pr[^o]*{,/**},
+ mount options=(rw,move) /pro[^c]*{,/**},
+ mount options=(rw,move) /proc?*{,/**},
+ mount options=(rw,move) /s[^y]*{,/**},
+ mount options=(rw,move) /sy[^s]*{,/**},
+ mount options=(rw,move) /sys?*{,/**},
+
+ # generated by: lxc-generate-aa-rules.py container-rules.base
+ deny /proc/sys/[^kn]*{,/**} wklx,
+ deny /proc/sys/k[^e]*{,/**} wklx,
+ deny /proc/sys/ke[^r]*{,/**} wklx,
+ deny /proc/sys/ker[^n]*{,/**} wklx,
+ deny /proc/sys/kern[^e]*{,/**} wklx,
+ deny /proc/sys/kerne[^l]*{,/**} wklx,
+ deny /proc/sys/kernel/[^smhd]*{,/**} wklx,
+ deny /proc/sys/kernel/d[^o]*{,/**} wklx,
+ deny /proc/sys/kernel/do[^m]*{,/**} wklx,
+ deny /proc/sys/kernel/dom[^a]*{,/**} wklx,
+ deny /proc/sys/kernel/doma[^i]*{,/**} wklx,
+ deny /proc/sys/kernel/domai[^n]*{,/**} wklx,
+ deny /proc/sys/kernel/domain[^n]*{,/**} wklx,
+ deny /proc/sys/kernel/domainn[^a]*{,/**} wklx,
+ deny /proc/sys/kernel/domainna[^m]*{,/**} wklx,
+ deny /proc/sys/kernel/domainnam[^e]*{,/**} wklx,
+ deny /proc/sys/kernel/domainname?*{,/**} wklx,
+ deny /proc/sys/kernel/h[^o]*{,/**} wklx,
+ deny /proc/sys/kernel/ho[^s]*{,/**} wklx,
+ deny /proc/sys/kernel/hos[^t]*{,/**} wklx,
+ deny /proc/sys/kernel/host[^n]*{,/**} wklx,
+ deny /proc/sys/kernel/hostn[^a]*{,/**} wklx,
+ deny /proc/sys/kernel/hostna[^m]*{,/**} wklx,
+ deny /proc/sys/kernel/hostnam[^e]*{,/**} wklx,
+ deny /proc/sys/kernel/hostname?*{,/**} wklx,
+ deny /proc/sys/kernel/m[^s]*{,/**} wklx,
+ deny /proc/sys/kernel/ms[^g]*{,/**} wklx,
+ deny /proc/sys/kernel/msg*/** wklx,
+ deny /proc/sys/kernel/s[^he]*{,/**} wklx,
+ deny /proc/sys/kernel/se[^m]*{,/**} wklx,
+ deny /proc/sys/kernel/sem*/** wklx,
+ deny /proc/sys/kernel/sh[^m]*{,/**} wklx,
+ deny /proc/sys/kernel/shm*/** wklx,
+ deny /proc/sys/kernel?*{,/**} wklx,
+ deny /proc/sys/n[^e]*{,/**} wklx,
+ deny /proc/sys/ne[^t]*{,/**} wklx,
+ deny /proc/sys/net?*{,/**} wklx,
+ deny /sys/[^fdck]*{,/**} wklx,
+ deny /sys/c[^l]*{,/**} wklx,
+ deny /sys/cl[^a]*{,/**} wklx,
+ deny /sys/cla[^s]*{,/**} wklx,
+ deny /sys/clas[^s]*{,/**} wklx,
+ deny /sys/class/[^nu]*{,/**} wklx,
+ deny /sys/class/n[^e]*{,/**} wklx,
+ deny /sys/class/ne[^t]*{,/**} wklx,
+ deny /sys/class/net?*{,/**} wklx,
+ deny /sys/class/u[^i]*{,/**} wklx,
+ deny /sys/class/ui[^o]*{,/**} wklx,
+ deny /sys/class?*{,/**} wklx,
+ deny /sys/d[^e]*{,/**} wklx,
+ deny /sys/de[^v]*{,/**} wklx,
+ deny /sys/dev[^i]*{,/**} wklx,
+ deny /sys/devi[^c]*{,/**} wklx,
+ deny /sys/devic[^e]*{,/**} wklx,
+ deny /sys/device[^s]*{,/**} wklx,
+# deny /sys/devices/[^vu]*{,/**} wklx,
+# deny /sys/devices/v[^i]*{,/**} wklx,
+# deny /sys/devices/vi[^r]*{,/**} wklx,
+# deny /sys/devices/vir[^t]*{,/**} wklx,
+# deny /sys/devices/virt[^u]*{,/**} wklx,
+# deny /sys/devices/virtu[^a]*{,/**} wklx,
+# deny /sys/devices/virtua[^l]*{,/**} wklx,
+# deny /sys/devices/virtual/[^n]*{,/**} wklx,
+# deny /sys/devices/virtual/n[^e]*{,/**} wklx,
+# deny /sys/devices/virtual/ne[^t]*{,/**} wklx,
+# deny /sys/devices/virtual/net?*{,/**} wklx,
+# deny /sys/devices/virtual?*{,/**} wklx,
+# deny /sys/devices?*{,/**} wklx,
+ deny /sys/f[^s]*{,/**} wklx,
+ deny /sys/fs/[^c]*{,/**} wklx,
+ deny /sys/fs/c[^g]*{,/**} wklx,
+ deny /sys/fs/cg[^r]*{,/**} wklx,
+ deny /sys/fs/cgr[^o]*{,/**} wklx,
+ deny /sys/fs/cgro[^u]*{,/**} wklx,
+ deny /sys/fs/cgrou[^p]*{,/**} wklx,
+ deny /sys/fs/cgroup?*{,/**} wklx,
+ deny /sys/fs?*{,/**} wklx,
+
+ ### Feature: unix
+ # Allow receive via unix sockets from anywhere
+ unix (receive),
+
+ # Allow all unix in the container
+ unix peer=(label=@{profile_name}),
+
+ ### Feature: cgroup namespace
+ mount fstype=cgroup -> /sys/fs/cgroup/**,
+
+ ### Feature: apparmor stacking
+
+ ### Configuration: apparmor loading disabled in privileged containers
+ deny /sys/k[^e]*{,/**} rwklx,
+ deny /sys/ke[^r]*{,/**} rwklx,
+ deny /sys/ker[^n]*{,/**} rwklx,
+ deny /sys/kern[^e]*{,/**} rwklx,
+ deny /sys/kerne[^l]*{,/**} rwklx,
+ deny /sys/kernel/[^sm]*{,/**} rwklx,
+ deny /sys/kernel/s[^e]*{,/**} rwklx,
+ deny /sys/kernel/se[^c]*{,/**} rwklx,
+ deny /sys/kernel/sec[^u]*{,/**} rwklx,
+ deny /sys/kernel/secu[^r]*{,/**} rwklx,
+ deny /sys/kernel/secur[^i]*{,/**} rwklx,
+ deny /sys/kernel/securi[^t]*{,/**} rwklx,
+ deny /sys/kernel/securit[^y]*{,/**} rwklx,
+ deny /sys/kernel/security/[^a]*{,/**} rwklx,
+ deny /sys/kernel/security/a[^p]*{,/**} rwklx,
+ deny /sys/kernel/security/ap[^p]*{,/**} rwklx,
+ deny /sys/kernel/security/app[^a]*{,/**} rwklx,
+ deny /sys/kernel/security/appa[^r]*{,/**} rwklx,
+ deny /sys/kernel/security/appar[^m]*{,/**} rwklx,
+ deny /sys/kernel/security/apparm[^o]*{,/**} rwklx,
+ deny /sys/kernel/security/apparmo[^r]*{,/**} rwklx,
+ deny /sys/kernel/security/apparmor?*{,/**} rwklx,
+ deny /sys/kernel/security?*{,/**} rwklx,
+ deny /sys/kernel?*{,/**} rwklx,
+}'''
+
+FN_VPP_DPDK_SCRIPT='/etc/vpp/startup.conf'
+
+TPL_VPP_DPDK_DAEMON_SCRIPT='''
+unix {
+ nodaemon
+ log /tmp/vpp.log
+ full-coredump
+}
+
+api-trace {
+ on
+}
+
+api-segment {
+ gid vpp
+}
+
+'''
+
+TPL_VPP_DPDK_SCRIPT='''
+unix {
+ log /tmp/vpp.log
+ full-coredump
+}
+
+api-trace {
+ on
+}
+
+api-segment {
+ gid vpp
+}
+
+'''
+
+APPARMOR_VPP_PROFILE = '''
+lxc.aa_profile = lxc-dpdk
+lxc.mount.entry = hugetlbfs dev/hugepages hugetlbfs rw,relatime,create=dir 0 0
+lxc.mount.auto = sys:rw'''
diff --git a/vicn/resource/vpp/vpp.py b/vicn/resource/vpp/vpp.py
new file mode 100644
index 00000000..f9d10703
--- /dev/null
+++ b/vicn/resource/vpp/vpp.py
@@ -0,0 +1,187 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017 Cisco and/or its affiliates.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import asyncio
+
+from netmodel.model.type import String, Integer, Bool
+from vicn.core.attribute import Attribute, Multiplicity
+from vicn.core.exception import ResourceNotFound
+from vicn.core.resource import Resource
+from vicn.core.task import BashTask, task, inline_task
+from vicn.resource.lxd.lxc_container import LxcContainer
+from vicn.resource.node import Node
+from vicn.resource.linux.file import TextFile
+from vicn.resource.vpp.dpdk_device import DpdkDevice
+from vicn.resource.vpp.scripts import FN_VPP_DPDK_SCRIPT
+from vicn.resource.vpp.scripts import TPL_VPP_DPDK_DAEMON_SCRIPT
+from vicn.resource.vpp.vpp_commands import CMD_VPP_DISABLE, CMD_VPP_STOP
+from vicn.resource.vpp.vpp_commands import CMD_VPP_START
+from vicn.resource.vpp.vpp_commands import CMD_VPP_ENABLE_PLUGIN
+
+#------------------------------------------------------------------------------
+# VPP forwarder
+#------------------------------------------------------------------------------
+
+CMD_GET = 'killall -0 vpp_main'
+CMD_DISABLE_IP_FORWARD = 'sysctl -w net.ipv4.ip_forward=0'
+
+class VPP(Resource):
+ """
+ Todo:
+ - make VPP an application with package install
+ - vpp should be a service (hence a singleton) for which we override the
+ start and stop commands
+ """
+
+ #__package_names__ = ['vpp', 'vpp-dbg', 'vpp-dpdk-dev']
+
+ plugins = Attribute(String,
+ multiplicity = Multiplicity.OneToMany)
+ node = Attribute(Node,
+ multiplicity = Multiplicity.OneToOne,
+ reverse_name = 'vpp')
+ numa_node = Attribute(Integer,
+ description = 'Numa node on which vpp will run')
+ core = Attribute(Integer,
+ description = 'Core belonging the numa node on which vpp will run')
+ enable_worker = Attribute(Bool,
+ description = 'Enable one worker for packet processing',
+ default = False)
+
+ #--------------------------------------------------------------------------
+ # Constructor and Accessors
+ #--------------------------------------------------------------------------
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+
+ self.vppctl_lock = asyncio.Lock()
+
+ self.dpdk_setup_file = None
+ if isinstance(self.node, LxcContainer):
+ if not 'vpp' in self.node.profiles:
+ self.node.profiles.append('vpp')
+
+ #--------------------------------------------------------------------------
+ # Resource lifecycle
+ #--------------------------------------------------------------------------
+
+ def __after__(self):
+ return ['BaseNetDevice']
+
+ def __get__(self):
+ return BashTask(self.node, CMD_GET)
+
+ def __subresources__(self):
+ self.dpdk_setup_file = TextFile(node = self.node,
+ filename = FN_VPP_DPDK_SCRIPT,
+ overwrite = True)
+ return self.dpdk_setup_file
+
+ def __create__(self):
+ socket_mem = dict()
+ numa_mgr = self.node.node_with_kernel.numa_mgr
+
+ for interface in self.node.interfaces:
+ if isinstance(interface, DpdkDevice):
+ # Assign as numa node the first numa node specified in a
+ # physical card (if any). If multiple nics connected to
+ # different numa nodes are assigned to this vpp memory access
+ # will be inefficient for the nics sitting in the other numa
+ # node.
+ socket_mem[interface.numa_node] = interface.socket_mem
+
+ for iface in self.interfaces:
+ if isinstance(iface.parent, DpdkDevice) and \
+ not iface.parent.numa_node is None:
+ self.numa_node = iface.parent.numa_node
+ break
+ if self.numa_node is None or self.core is None:
+ self.numa_node, self.core = \
+ numa_mgr.get_numa_core(numa_node = self.numa_node)
+
+ dpdk_list = list()
+
+ # On numa architecture socket-mem requires to set the amount of memory
+ # to be reserved on each numa node
+ socket_mem_str = 'socket-mem '
+ for numa in range (0,numa_mgr.get_number_of_numa()):
+ if numa in socket_mem:
+ socket_mem_str = socket_mem_str + str(socket_mem[numa])
+ else:
+ socket_mem_str = socket_mem_str + '0'
+
+ if numa < numa_mgr.get_number_of_numa()-1:
+ socket_mem_str = socket_mem_str + ','
+
+ dpdk_list.append(socket_mem_str)
+
+ for interface in self.node.interfaces:
+ if isinstance(interface, DpdkDevice):
+ dpdk_list.append('dev ' + interface.pci_address)
+
+ # Add the core on which running vpp and the dpdk parameters
+ setup = TPL_VPP_DPDK_DAEMON_SCRIPT + 'cpu {'
+
+ setup = setup + ''' \n main-core ''' + str(self.core)
+
+ if self.enable_worker:
+ self.numa_node, cpu_worker =numa_mgr.get_numa_core(self.numa_node)
+ setup = setup + '''\n corelist-workers ''' + str(cpu_worker)
+
+ setup = setup + '''\n}\n\n dpdk { '''
+
+ for dpdk_dev in dpdk_list:
+ setup = setup + ''' \n ''' + dpdk_dev
+
+ setup = setup + '\n}'
+
+
+ if any([isinstance(interface,DpdkDevice) for interface in self.node.interfaces]):
+ self.dpdk_setup_file.content = setup
+ else:
+ self.dpdk_setup_file.content = TPL_VPP_DPDK_DAEMON_SCRIPT
+
+ lock = self.node.node_with_kernel.vpp_host.vppstart_lock
+
+ vpp_disable = BashTask(self.node, CMD_VPP_DISABLE, lock = lock)
+ vpp_stop = BashTask(self.node, CMD_VPP_STOP, lock = lock)
+ enable_ip_forward = BashTask(self.node, CMD_DISABLE_IP_FORWARD)
+ start_vpp = BashTask(self.node, CMD_VPP_START, lock = lock)
+
+ return ((vpp_disable > vpp_stop) | enable_ip_forward) > start_vpp
+
+ def __delete__(self):
+ return BashTask(self.node, CMD_VPP_STOP)
+
+ def _add_plugins(self, plugin):
+ return BashTask(self.node, CMD_VPP_ENABLE_PLUGIN, {'plugin': plugin})
+
+ def _set_plugins(self):
+ cmd = None
+ for plugin in self.plugins:
+ cmd = cmd > BashTask(self.node, CMD_VPP_ENABLE_PLUGIN,
+ {'plugin' : plugin})
+ return cmd
+
+ def _remove_plugins(self, plugin):
+ raise NotImplementedError
+
+ @inline_task
+ def _get_plugins(self):
+ return {'plugins' : []}
diff --git a/vicn/resource/vpp/vpp_bridge.py b/vicn/resource/vpp/vpp_bridge.py
new file mode 100644
index 00000000..c7a70c02
--- /dev/null
+++ b/vicn/resource/vpp/vpp_bridge.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017 Cisco and/or its affiliates.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+from netmodel.model.type import Integer
+from vicn.core.attribute import Attribute, Multiplicity
+from vicn.core.attribute import Reference
+from vicn.core.exception import ResourceNotFound
+from vicn.core.requirement import Requirement
+from vicn.core.resource_mgr import wait_resource_task
+from vicn.core.resource import Resource
+from vicn.core.task import task, BashTask, EmptyTask
+from vicn.resource.channel import Channel
+from vicn.resource.linux.application import LinuxApplication
+from vicn.resource.linux.sym_veth_pair import SymVethPair
+from vicn.resource.linux.sym_veth_pair import SymVethPair
+from vicn.resource.node import Node
+from vicn.resource.vpp.dpdk_device import DpdkDevice
+from vicn.resource.vpp.interface import VPPInterface
+from vicn.resource.vpp.vpp import VPP
+
+CMD_ADD_INTERFACE_TO_BR = ('vppctl set interface l2 bridge '
+ '{interface.device_name} {br_domain}')
+
+class VPPBridge(Channel, LinuxApplication):
+ """
+ Resource: VPPBridge
+
+ VPPBridge instantiate a vpp resource and set it as a vpp bridge.
+
+ This resource requires to be run within a LxcContainer which will have VPP.
+ Every interface in the lxc_container (i.e., the ones contained in
+ self.node.interfaces) will be added to the vpp bridge. To connect other vpp
+ node to the bridge, the corresponding dpdkdevice must be added as an
+ interface to the channel.
+ """
+
+ # The vpp bridge _USES_ a VPP forwarder on the node
+ node = Attribute(Node, mandatory=True,
+ description = 'Node on which vpp is running',
+ requirements = [Requirement('vpp')])
+
+ connected_nodes = Attribute(Node, multiplicity = Multiplicity.OneToMany,
+ description = 'List of nodes to connect to the bridge')
+
+ #--------------------------------------------------------------------------
+ # Constructor and Accessors
+ #--------------------------------------------------------------------------
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self._vpp_interfaces = list()
+
+ #--------------------------------------------------------------------------
+ # Resource lifecycle
+ #--------------------------------------------------------------------------
+
+ def __subresources__ (self):
+ # We don't need any reference to the list of SymVethPair because each
+ # side of a veth will be included in the node.interfaces list
+ self._veths = [SymVethPair(node1 = self.node, node2 = node,
+ owner = self) for node in self.connected_nodes]
+
+ return Resource.__concurrent__(*self._veths)
+
+ @task
+ def __initialize__ (self):
+ # Add the veth side on the connected_nodes to the set of interfaces of
+ # the channel
+ self.interfaces.extend([veth.side2 for veth in self._veths])
+
+ @task
+ def __get__(self):
+ # Forces creation
+ raise ResourceNotFound
+
+ # Nothing to do
+ __delete__ = None
+
+ def __create__(self):
+ manager = self._state.manager
+
+ # Create a VPPInterface for each interface in the node. These will be
+ # the interfaces we will connect to the vpp bridge process
+ vpp_interfaces = list()
+ for interface in self.node.interfaces:
+ # FIXME harcoded value
+ if interface.device_name == 'eth0':
+ continue
+
+ vpp_interface = VPPInterface(vpp = self.node.vpp,
+ parent = interface,
+ ip_address = Reference(interface, 'ip_address'),
+ device_name = 'host-' + interface.device_name)
+ vpp_interfaces.append(vpp_interface)
+ manager.commit_resource(vpp_interface)
+
+ tasks = EmptyTask()
+
+ for vpp_interface in vpp_interfaces:
+ tasks = tasks > (wait_resource_task(vpp_interface) >
+ self._add_interface(vpp_interface,0))
+
+ return wait_resource_task(self.node.vpp) > tasks
+
+ #--------------------------------------------------------------------------
+ # Internal methods
+ #--------------------------------------------------------------------------
+
+ def _add_interface(self, interface, br_domain):
+ return BashTask(self.node, CMD_ADD_INTERFACE_TO_BR,
+ {'interface': interface, 'br_domain': br_domain})
+
+ def _del_interface(self, interface, br_domain):
+ raise NotImplementedError('Interface removal not supported')
+
diff --git a/vicn/resource/vpp/vpp_commands.py b/vicn/resource/vpp/vpp_commands.py
new file mode 100644
index 00000000..8ee64bf6
--- /dev/null
+++ b/vicn/resource/vpp/vpp_commands.py
@@ -0,0 +1,41 @@
+##### VPP SETUP #####
+
+CMD_VPP_STOP_SERVICE = 'systemctl stop vpp.service'
+CMD_VPP_DISABLE = 'systemctl disable vpp.service'
+
+# 'sleep 1' ensures that VPP has enough time to start
+CMD_VPP_START = '''
+systemctl start vpp
+sleep 1
+'''
+CMD_VPP_STOP = '''
+systemctl stop vpp
+killall -9 vpp_main || true
+'''
+CMD_VPP_ENABLE_PLUGIN = 'vppctl {plugin} enable'
+
+##### VPP INTERFACES #####
+
+CMD_VPP_CREATE_IFACE = '''
+# Create vpp interface from netmodel.network.interface.device_name} with mac {self.parent.mac_address}
+vppctl create host-interface name {vpp_interface.parent.device_name} hw-addr {vpp_interface.parent.mac_address}
+vppctl set interface state {vpp_interface.device_name} up
+'''
+CMD_VPP_SET_IP = 'vppctl set int ip address {netdevice.device_name} {netdevice.ip_address}/{netdevice.prefix_len}'
+CMD_VPP_SET_UP = 'vppctl set int state {netdevice.device_name} up'
+
+##### VPP IP ROUTING #####
+
+CMD_VPP_ADD_ROUTE = 'vppctl set ip arp static {route.interface.vppinterface.device_name} {route.ip_address} {route.mac_address}'
+CMD_VPP_DEL_ROUTE = 'vppctl set ip arp del static {route.interface.vppinterface.device_name} {route.ip_address} {route.mac_address}'
+CMD_VPP_ADD_ROUTE_GW = 'vppctl ip route add {route.ip_address}/32 via {route.gateway} {route.interface.vppinterface.device_name}'
+CMD_VPP_DEL_ROUTE_GW = 'vppctl ip route del {route.ip_address}/32 via {route.gateway} {route.interface.vppinterface.device_name}'
+
+##### VPP CICN PLUGIN #####
+
+CMD_VPP_CICN_GET = "timeout 1 vppctl cicn show" #We timeout if vpp is not started
+CMD_VPP_ADD_ICN_ROUTE = 'vppctl cicn cfg fib add prefix {route.prefix} face {route.face.id}'
+CMD_VPP_ADD_ICN_FACE = 'vppctl cicn cfg face add local {face.src_ip}:{face.src_port} remote {face.dst_ip}:{face.dst_port}'
+
+CMD_VPP_CICN_GET_CACHE_SIZE = 'vppctl cicn show | grep "CS entries" | grep -E "[0-9]+"'
+CMD_VPP_CICN_SET_CACHE_SIZE = 'vppctl cicn control param cs size {self.cache_size}'
diff --git a/vicn/resource/vpp/vpp_host.py b/vicn/resource/vpp/vpp_host.py
new file mode 100644
index 00000000..134e65b0
--- /dev/null
+++ b/vicn/resource/vpp/vpp_host.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017 Cisco and/or its affiliates.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import asyncio
+
+from netmodel.model.type import String
+from vicn.core.attribute import Attribute, Multiplicity
+from vicn.core.exception import ResourceNotFound
+from vicn.core.requirement import Requirement
+from vicn.core.task import BashTask, task, EmptyTask
+from vicn.resource.linux.application import LinuxApplication
+from vicn.resource.linux.file import TextFile
+from vicn.resource.node import Node
+from vicn.resource.vpp.scripts import FN_APPARMOR_DPDK_SCRIPT
+from vicn.resource.vpp.scripts import TPL_APPARMOR_DPDK_SCRIPT
+from vicn.resource.vpp.scripts import FN_VPP_DPDK_SCRIPT
+from vicn.resource.vpp.scripts import TPL_VPP_DPDK_DAEMON_SCRIPT
+from vicn.resource.vpp.vpp_commands import CMD_VPP_DISABLE
+from vicn.resource.vpp.vpp_commands import CMD_VPP_STOP_SERVICE
+
+CMD_INSERT_MODULES = 'modprobe uio && modprobe igb_uio'
+CMD_APP_ARMOR_RELOAD = '''
+# Force apparmor to reload profiles to include the new profile
+/etc/init.d/apparmor reload
+'''
+CMD_SYSCTL_HUGEPAGES = 'sysctl -w vm.nr_hugepages={nb_hp}'
+DEFAULT_NB_HUGEPAGES = 1024
+CMD_GREP_UIO_DEV = 'ls /dev | grep uio'
+CMD_CREATE_UIO_DEVICES = "dpdk_nic_bind --bind=igb_uio {pci_address}"
+
+class VPPHost(LinuxApplication):
+ """
+ Resource: VPPHost
+
+ Only used for container deployment
+
+ Packages required on the host
+ - vpp : sysctl configuration
+ - vpp-dpdk-dkms : kernel modules
+
+ Host must be configured to let vpp to work into container:
+ - install new apparmor profile (to let the container to read
+ hugepages info in /sys/kernel/mm/hugepages)
+ - set hugepages into the host
+ """
+
+ node = Attribute(Node,
+ description = 'Node on which the application is installed',
+ mandatory = True,
+ multiplicity = Multiplicity.OneToOne,
+ reverse_name = 'vpp_host',
+ reverse_description = 'Setup for hosting vpp containers',
+ reverse_auto = True,
+ requirements = [Requirement('numa_mgr')])
+ uio_devices = Attribute(String,
+ description = 'uio devices on the node',
+ multiplicity = Multiplicity.OneToMany,
+ ro = True)
+ dpdk_devices = Attribute(String,
+ description = 'Dpdk devices on the node',
+ multiplicity = Multiplicity.OneToMany)
+
+ __package_names__ = ['dpdk', 'vpp', 'vpp-dpdk-dkms']
+
+ #--------------------------------------------------------------------------
+ # Constructor and Accessors
+ #--------------------------------------------------------------------------
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.vppstart_lock = asyncio.Lock()
+
+ #--------------------------------------------------------------------------
+ # Resource lifecycle
+ #--------------------------------------------------------------------------
+
+ def __subresources__(self):
+ app_armor_file = TextFile(node = self.node,
+ filename = FN_APPARMOR_DPDK_SCRIPT,
+ content = TPL_APPARMOR_DPDK_SCRIPT,
+ overwrite = True)
+ startup_conf = TextFile(node = self.node,
+ filename = FN_VPP_DPDK_SCRIPT,
+ content = TPL_VPP_DPDK_DAEMON_SCRIPT,
+ overwrite = True)
+ return app_armor_file | startup_conf
+
+ @task
+ def __get__(self):
+ """
+ This method always assumes the resource does not exist, since it is not
+ an issue to perform the modprobe call everytime.
+ """
+ raise ResourceNotFound
+
+ def __create__(self):
+ modules = BashTask(self.node, CMD_INSERT_MODULES)
+ app_armor_reload = BashTask(self.node, CMD_APP_ARMOR_RELOAD)
+ sysctl_hugepages = BashTask(self.node, CMD_SYSCTL_HUGEPAGES,
+ {'nb_hp': DEFAULT_NB_HUGEPAGES})
+
+ # Hook
+ # The following is needed to create uio devices in /dev. They are
+ # required to let vpp to use dpdk (or other compatibles) nics. From a
+ # container, vpp cannot create those devices, therefore we need to
+ # create them in the host and then mount them on each container running
+ # vpp (and using a physical nic)
+ stop_vpp = BashTask(self.node, CMD_VPP_STOP_SERVICE)
+ disable_vpp = BashTask(self.node, CMD_VPP_DISABLE)
+ disable_vpp = stop_vpp > disable_vpp
+
+ create_uio = EmptyTask()
+ for device in self.dpdk_devices:
+ create_uio = create_uio > BashTask(self.node,
+ CMD_CREATE_UIO_DEVICES, {'pci_address' : device})
+
+ return ((modules | app_armor_reload) | sysctl_hugepages) > \
+ (disable_vpp > create_uio)
+
+ __delete__ = None
+
+ #--------------------------------------------------------------------------
+ # Attributes
+ #--------------------------------------------------------------------------
+
+ def _get_uio_devices(self):
+ def parse(rv):
+ return rv.stdout.splitlines()
+ return BashTask(self.node, CMD_GREP_UIO_DEV, parse = parse)