Compare commits

...

974 Commits

Author SHA1 Message Date
ad10b6fa91 Merge pull request #484 from squeed/disable-ra
ptp, bridge: disable accept_ra on the host-side interface
2020-05-13 09:43:58 +02:00
219eb9e046 ptp, bridge: disable accept_ra on the host-side interface
The interface plugins should have absolute control over their addressing
and routing.

Signed-off-by: Casey Callendrello <cdc@redhat.com>
2020-05-12 15:54:23 +02:00
f7a2fc97e4 Merge pull request #460 from timyinshi/windowcontainer-new
modify the error url of windowscontainer
2020-04-29 16:58:32 +01:00
6957f6ca4e Merge pull request #479 from Nordix/issue-478
plugins/meta/sbr: Adjusted ipv6 address mask to /128
2020-04-29 17:15:54 +02:00
02bfece2e9 plugins/meta/sbr: Adjusted ipv6 address mask to /128
A /64 mask was used which routed an entire cidr based on source,
not only the bound address.

Fixes #478

Signed-off-by: Lars Ekman <lars.g.ekman@est.tech>
2020-04-28 16:38:35 +02:00
5af9ff493e Merge pull request #469 from AlbanBedel/portmap-hairpin-subnet
portmap: Apply the DNAT hairpin to the whole subnet
2020-04-22 17:22:14 +02:00
44d92c19de Merge pull request #475 from vboulineau/vboulineau/hostport_windows
win-bridge: add support for portMappings capability
2020-04-22 17:11:25 +02:00
5e0fbd8374 portmap: Apply the DNAT hairpin to the whole subnet
The DNAT hairpin rule only allow the container itself to access the
ports it is exposing thru the host IP. Other containers in the same
subnet might also want to access this service via the host IP, so
apply this rule to the whole subnet instead of just for the container.

This is particularly useful with setups using a reverse proxy for
https. With such a setup connections between containers (for ex.
oauth2) have to downgrade to http, or need complex dns setup to make
use of the internal IP of the reverse proxy. On the other hand going
thru the host IP is easy as that is probably what the service name
already resolve to.

Signed-off-by: Alban Bedel <albeu@free.fr>
--
v2: Fixed the tests
v3: Updated iptables rules documentation in README.md
v4: Fixed the network addresses in README.md to match iptables output
2020-04-17 16:27:57 +02:00
a78853f29f Support device id in host device plugin (#471)
* Add support for `deviceID` runtime config attribute

Signed-off-by: Adrian Chiris <adrianc@mellanox.com>
2020-04-15 10:45:14 -05:00
2d2583ee33 win-bridge: add support for portMappings capability
If the pluging receives portMappings in runtimeConfig, the pluing will add a NAT policy for each port mapping on the generated endpoints.
It enables HostPort usage on Windows with win-bridge.

Signed-off-by: Vincent Boulineau <vincent.boulineau@datadoghq.com>
2020-04-15 15:01:32 +02:00
f4332fec59 Merge pull request #468 from hongli-my/fix-port-state
check bridge's port state
2020-04-08 11:58:56 -04:00
ed16760739 Merge pull request #472 from ahenan/master
Reset the route flag before moving the rule
2020-04-08 17:55:59 +02:00
30776ff858 check bridge's port state
fix #463
link host veth pair to bridge, the Initial state
of port is BR_STATE_DISABLED and change to
BR_STATE_FORWARDING async.

Signed-off-by: honglichang <honglichang@tencent.com>
2020-04-08 14:15:50 +08:00
2a48d68937 Reset the route flag before moving the rule
Signed-off-by: ahenan <ahenan00@gmail.com>
2020-04-06 17:12:45 +02:00
117e30ff21 Merge pull request #458 from mars1024/remove/jujuerrors
replace juju/errors because of CNCF license scan
2020-04-01 18:01:32 +02:00
486ef96e6f [DO NOT REVIEW] vendor upate to remove useless dependencies
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2020-03-17 14:30:28 +08:00
8a0e3fe10e build error utility package to replace juju/errors
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2020-03-11 20:33:21 +08:00
ca419073e4 modify the error url of windowscontainer
Signed-off-by: root <timyinshi>
2020-03-05 09:38:33 +08:00
47a9fd80c8 Merge pull request #455 from booxter/master
Unlock OS thread after netns is restored
2020-03-04 11:34:44 -05:00
112288ecb2 Unlock OS thread after netns is restored
The current ns package code is very careful about not leaving the calling
thread with the overridden namespace set, for example when origns.Set() fails.
This is achieved by starting a new green thread, locking its OS thread, and
never unlocking it. Which makes golang runtime to scrap the OS thread backing
the green thread after the go routine exits.

While this works, it's probably not as optimal: stopping and starting a new OS
thread is expensive and may be avoided if we unlock the thread after resetting
network namespace to the original. On the other hand, if resetting fails, it's
better to leave the thread locked and die.

While it won't work in all cases, we can still make an attempt to reuse the OS
thread when resetting the namespace succeeds. This can be achieved by unlocking
the thread conditionally to the namespace reset success.

Signed-off-by: Ihar Hrachyshka <ihrachys@redhat.com>
2020-02-20 17:24:36 -05:00
32fc3ee9d3 Merge pull request #454 from dcbw/update-coreos-owners
owners: updates for maintainer changes
2020-02-19 17:29:54 +01:00
c7e2cf7602 owners: updates for maintainer changes
Add Michael Cambria per https://github.com/containernetworking/cni/pull/751
Remove Stefan Junker per personal request
Update Casey's email to @redhat.com

Signed-off-by: Dan Williams <dcbw@redhat.com>
2020-02-19 10:23:21 -06:00
5c512194eb Merge pull request #453 from Nordix/nfvi_virtio
Make host-device to work with virtio net device
2020-02-18 11:22:03 +01:00
a9b4e04bc4 Make host-device to work with virtio net device
In case pciBusID contains pci address of the virtio device,
then lookup the net directory under virtio<id> directory.

Issue: https://github.com/containernetworking/plugins/issues/320

Signed-off-by: Periyasamy Palanisamy <periyasamy.palanisamy@est.tech>
2020-02-11 18:05:37 +01:00
f5c3d1b1ba Merge pull request #443 from mars1024/bugfix/black_box_test
pkg/utils: sysctl package should use black-box testing
2020-01-29 17:26:04 +01:00
8bf6a7b362 Merge pull request #444 from mars1024/bugfix/ptp_redundant
ptp: remove some redundant lines
2020-01-29 10:23:06 -06:00
66e0aaf9c1 Merge pull request #445 from mars1024/bugfix/link_del
pkg/ip: use type cast instead of untrusty error message
2020-01-29 10:20:32 -06:00
45fd949465 ptp: remove some redundant lines
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2020-01-29 20:17:01 +08:00
2ff84a481e pkg/ip: use type cast instead of untrusty error message
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2020-01-29 20:03:15 +08:00
37207f05b4 pkg/utils: sysctl package should use black-box testing
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2020-01-27 21:09:04 +08:00
832f2699c2 Merge pull request #442 from ofiliz/loopback-fix
loopback: Fix ipv6 address checks
2020-01-27 09:38:52 +01:00
d1360b82ab loopback: Fix ipv6 address checks
Signed-off-by: Onur Filiz <ofiliz@users.noreply.github.com>
2020-01-23 17:33:47 -08:00
1f33fb729a Merge pull request #438 from tnqn/vlan-multi-del
Fix vlan plugin returning error when device is already removed
2020-01-22 17:18:18 +01:00
754e153b03 Merge pull request #437 from jcaamano/master
Improve support of sysctl name separators
2020-01-22 17:14:46 +01:00
0edf8a4208 Merge pull request #434 from carlosedp/vlanfilter
Fix for the case where kernel doesn't have CONFIG_BRIDGE_VLAN_FILTERING
2020-01-15 16:51:53 +00:00
db7e6cfabf Fix vlan plugin returning error when device is already removed
DEL can be called multiple times, a plugin should return no error if
the device is already removed, and other errors should be returned. It
was the opposite for vlan plugin. This PR fixes it.

Signed-off-by: Quan Tian <qtian@vmware.com>
2020-01-14 23:19:22 -08:00
963aaf86e6 Format with gofmt
Signed-off-by: Jaime Caamaño Ruiz <jcaamano@suse.com>
2020-01-13 19:44:40 +01:00
cd9d6b28da Use Replace instead of ReplaceAll
Signed-off-by: Jaime Caamaño Ruiz <jcaamano@suse.com>
2020-01-13 16:50:13 +01:00
0452c1dd10 Fix copyrights
Signed-off-by: Jaime Caamaño Ruiz <jcaamano@suse.com>
2020-01-13 14:56:58 +01:00
d671d29ad5 Improve support of sysctl name seprators
Sysctl names can use dots or slashes as separator:

- if dots are used, dots and slashes are interchanged.
- if slashes are used, slashes and dots are left intact.

Separator in use is determined by firt ocurrence.

Reference: http://man7.org/linux/man-pages/man5/sysctl.d.5.html

Signed-off-by: Jaime Caamaño Ruiz <jcaamano@suse.com>
2020-01-13 14:40:42 +01:00
cc6154603e Fix for the case for kernels without CONFIG_BRIDGE_VLAN_FILTERING
If the Linux kernel is not built with the parameter
CONFIG_BRIDGE_VLAN_FILTERING, passing vlanFiltering in
the Bridge struct returns an error creating the bridge interface.
This happens even when no parameter is set on Vlan in the CNI config.

This change fixes the case where no Vlan parameter is configured on
CNI config file so the flag doesn't need to be included in the struct.

Signed-off-by: Carlos de Paula <me@carlosedp.com>
2020-01-10 09:17:54 -03:00
62b36d2fbc Merge pull request #433 from ydcool/mips64le-support
add support for mips64le
2020-01-08 16:26:13 +00:00
e5fdd449dd Merge pull request #428 from weibeld/master
bridge: add missing cniVersion in README example
2020-01-08 16:24:48 +00:00
8db5e4d41b add support for mips64le
Signed-off-by: Dominic Yin <yindongchao@inspur.com>
2020-01-06 15:51:47 +08:00
ec8f6c99d0 Merge pull request #421 from aojea/portmapErrors2
Portmap doesn't fail if chain doesn't exist
2019-12-19 00:16:58 +08:00
7dea2a4c1b Add missing cniVersion in README example
Signed-off-by: Daniel Weibel <danielmweibel@gmail.com>
2019-12-18 19:08:37 +08:00
5a02c5bc61 bump go-iptables module to v0.4.5
bump the go-iptables module to v0.4.5 to avoid
concurrency issues with the portmap plugin and
errors related to iptables not able to hold the
lock.

Signed-off-by: Antonio Ojea <antonio.ojea.garcia@gmail.com>
2019-12-16 17:42:20 +01:00
bf8f171041 iptables: add idempotent functions
Add the following idempotent functions to iptables utils:

DeleteRule: idempotently delete an iptables rule
DeleteChain: idempotently delete an iptables chain
ClearChain: idempotently flush an iptables chain

Signed-off-by: Antonio Ojea <antonio.ojea.garcia@gmail.com>
2019-12-12 15:13:15 +01:00
3603738c6a portmap doesn't fail if chain doesn't exist
It turns out that the portmap plugin is not idempotent if its
executed in parallel.
The errors are caused due to a race of different instantiations
deleting the chains.
This patch does that the portmap plugin doesn't fail if the
errors are because the chain doesn't exist on teardown.

Signed-off-by: Antonio Ojea <antonio.ojea.garcia@gmail.com>
2019-12-12 09:03:06 +01:00
d8b1289098 fix portmap port forward flakiness
Use a Describe container for the It code block of the
portmap port forward integration test.

Signed-off-by: Antonio Ojea <antonio.ojea.garcia@gmail.com>
2019-12-12 09:03:06 +01:00
6551165853 Merge pull request #412 from containernetworking/new-maintainers
Add Bruce Ma and Piotr Skarmuk as owners
2019-12-04 10:11:31 -06:00
10a01b09ae Add Bruce Ma and Piotr Skarmuk as owners
Signed-off-by: Bryan Boreham <bryan@weave.works>
2019-11-16 11:45:44 +00:00
497560f35f Merge pull request #408 from tgross/idempotent_chain_creation
ensure iptables chain creation is idempotent
2019-11-13 17:20:45 +01:00
58dd90b996 ensure iptables chain creation is idempotent
Concurrent use of the `portmap` and `firewall` plugins can result in
errors during iptables chain creation:

- The `portmap` plugin has a time-of-check-time-of-use race where it
  checks for existence of the chain but the operation isn't atomic.
- The `firewall` plugin doesn't check for existing chains and just
  returns an error.

This commit makes both operations idempotent by creating the chain and
then discarding the error if it's caused by the chain already
existing. It also factors the chain creation out into `pkg/utils` as a
site for future refactoring work.

Signed-off-by: Tim Gross <tim@0x74696d.com>
2019-11-11 10:00:11 -05:00
d5efdfe1f6 Merge pull request #409 from squeed/fix-integ-tests
integration: fix ip address collision in integration tests
2019-11-11 14:07:52 +01:00
05f121a406 integration: fix ip address collision in integration tests
Signed-off-by: Casey Callendrello <cdc@redhat.com>
2019-11-11 13:36:21 +01:00
825fbd8a95 Merge pull request #405 from mars1024/feat/vlan_mtu_validation
vlan: add MTU validation to loadNetConf
2019-11-06 16:29:57 +00:00
1a30688da0 add some testcases about invalid MTUs
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-10-25 20:15:18 +08:00
bee8d6cf30 vlan: add MTU validation in loadNetConf
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-10-25 19:59:33 +08:00
a16232968d Merge pull request #400 from s1061123/fix/overwrite-ips
static: prioritize the input sources for IPs
2019-10-23 16:54:54 +01:00
1880421389 Merge pull request #401 from giuseppe/run-in-a-userns
testutils: newNS() works in a rootless user namespace
2019-10-23 16:29:59 +01:00
a2ed3d9a69 Merge pull request #403 from s1061123/dev/addgarp
Sending GratuitousArp in case of MAC address update
2019-10-23 16:24:58 +01:00
7bcaae263f Merge pull request #404 from mars1024/feat/mtu_validation
macvlan: add MTU validation to loadNetConf
2019-10-23 16:13:06 +01:00
e1f955d9bf macvlan: add MTU validation to loadNetConf
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-10-23 20:39:14 +08:00
2583a0b4ad Sending GratuitousArp in case of MAC address update
This change sends gratuitous ARP when MAC address is changed to
let other devices to know the MAC address update.

Signed-off-by: Tomofumi Hayashi <tohayash@redhat.com>
2019-10-23 15:17:38 +09:00
85083ea434 testutils: newNS() works in a rootless user namespace
When running in a user namespace created by an unprivileged user the
owner of /var/run will be reported as the unknown user (as defined in
/proc/sys/kernel/overflowuid) so any access to the directory will
fail.

If the XDG_RUNTIME_DIR environment variable is set, check whether the
current user is also the owner of /var/run.  If the owner is different
than the current user, use the $XDG_RUNTIME_DIR/netns directory.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2019-10-19 12:04:53 +02:00
2290fc8d8a static: prioritize the input sources for IPs
This change introduce priorities for IPs input among CNI_ARGS,
'args' and runtimeConfig. Fix #399.

Signed-off-by: Tomofumi Hayashi <tohayash@redhat.com>
2019-10-17 13:36:49 +09:00
411d060b81 Merge pull request #389 from CallMeFoxie/bw-units
Use uint64 for Bandwidth plugin
2019-10-09 16:25:06 +01:00
5915b49b38 Merge pull request #394 from mars1024/bugfix/validate_vlanid
bridge: check vlan id when loading net conf
2019-10-09 17:23:47 +02:00
c25c62742b Merge pull request #396 from oshothebig/contributing-doc
contributing doc: revise test script name to run
2019-10-09 10:21:03 -05:00
b7ffa24326 vlan/bridge: fix some typo
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-10-08 11:57:30 +08:00
15894b36a0 Merge pull request #397 from oshothebig/install-cnitool
contributing doc: describe cnitool installation
2019-10-07 14:27:23 +02:00
77b51f0bc9 contributing doc: describe cnitool installation
cnitool must be installed before running tests because cnitool is
invoked during the tests

Signed-off-by: Sho SHIMIZU <sho.shimizu@gmail.com>
2019-10-07 17:42:44 +09:00
bd63528b0b contributing doc: revise test script name to run
test.sh doesn't exists now as it was separated into two OS-specific
scripts in 4e1f7802db.

Signed-off-by: Sho SHIMIZU <sho.shimizu@gmail.com>
2019-10-04 18:32:12 +09:00
cf187287af Update tests for uint64
Signed-off-by: Ashley Reese <ashley@victorianfox.com>
2019-10-03 16:55:41 +02:00
0dff883769 Use uint64 for Bandwidth plugin
Signed-off-by: Ashley Reese <ashley@victorianfox.com>
2019-10-03 16:05:27 +02:00
d0eeb27494 Merge pull request #390 from sipsma/firewall-fix
firewall: don't return error in DEL if prevResult is not found.
2019-10-02 10:38:47 -05:00
e70558cbe1 bridge: check vlan id when loading net conf
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-09-30 17:12:31 +08:00
0a1421a08c firewall: remove unused netns check from DEL method
Signed-off-by: Erik Sipsma <sipsma@amazon.com>
2019-09-25 20:38:02 +00:00
0f19aa2f8d Merge pull request #388 from sipsma/fix-ptpdns
ptp: only override DNS conf if DNS settings provided
2019-09-25 17:43:24 +02:00
e91889678b Merge pull request #391 from beautytiger/dev-190925
bugfix: defer after err check, or it may panic
2019-09-25 16:25:21 +01:00
8ec6bd6a42 bugfix: defer after err check, or it may panic
Signed-off-by: Guangming Wang <guangming.wang@daocloud.io>
2019-09-25 22:21:49 +08:00
fc7059c1ae firewall: don't return error in DEL if prevResult is not found.
The CNI spec states that for DEL implementations, "when CNI_NETNS and/or
prevResult are not provided, the plugin should clean up as many resources as
possible (e.g. releasing IPAM allocations) and return a successful response".
This change results in the firewall plugin conforming to the spec by not
returning an error whenever the del method is not provided a prevResult.

Signed-off-by: Erik Sipsma <sipsma@amazon.com>
2019-09-23 21:11:07 +00:00
a96c469e62 ptp: only override DNS conf if DNS settings provided
Previously, if an IPAM plugin provided DNS settings in the result to the PTP
plugin, those settings were always lost because the PTP plugin would always
provide its own DNS settings in the result even if the PTP plugin was not
configured with any DNS settings.

This was especially problematic when trying to use, for example, the host-local
IPAM plugin's support for retrieving DNS settings from a resolv.conf file on
the host. Before this change, those DNS settings were always lost when using the
PTP plugin and couldn't be specified as part of PTP instead because PTP does not
support parsing a resolv.conf file.

This change checks to see if any fields were actually set in the PTP plugin's
DNS settings and only overrides any previous DNS results from an IPAM plugin in
the case that settings actually were provided to PTP. In the case where no
DNS settings are provided to PTP, the DNS results of the IPAM plugin (if any)
are used instead.

Signed-off-by: Erik Sipsma <sipsma@amazon.com>
2019-09-18 21:09:22 +00:00
291ab6cc84 Merge pull request #386 from janisz/patch-1
Bump Go version
2019-09-18 17:12:43 +02:00
90125f40ba Bump Go version
Signed-off-by: Tomasz Janiszewski <janiszt@gmail.com>
2019-09-18 11:20:33 +02:00
23d5525ec3 Merge pull request #383 from mccv1r0/issue381
When prevResults are not supplied to loopback plugin, create results to return
2019-09-11 11:11:55 -05:00
fd42109a06 When prevResults are not returned to loopback plugin, create results to return based on
the lo interface and IP address assigned inside container.

Signed-off-by: Michael Cambria <mcambria@redhat.com>
2019-09-11 11:57:03 -04:00
4bb288193c Merge pull request #379 from xcelsion/fix-host-container-address-family-mismatch
Fix dual-stack support in meta/portmap
2019-09-11 17:16:36 +02:00
e8365e126d Fixed issue where hostIP address family was not checked against the containerIP address family. closes #378
Signed-off-by: Niels van Oosterom <xcelsion@users.noreply.github.com>
2019-09-06 15:23:00 +02:00
7e68430081 Merge pull request #377 from mars1024/bumpup/to/0.7.1
bump up libcni to v0.7.1
2019-08-28 10:55:43 -05:00
f81a529ebd Merge pull request #375 from smarkm/master
Fixes #342, cleanup netns after test suite
2019-08-28 17:55:36 +02:00
630a4d8db6 Merge pull request #374 from mars1024/feat/loopback_support_check
loopback support CNI CHECK and result cache
2019-08-28 10:53:43 -05:00
3d56f7504d loopback plugin support to pass previous result transpartently
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-08-23 22:56:22 +08:00
659a09f34e loopback support CNI CHECK
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-08-23 20:36:37 +08:00
b76ace9c64 bump up libcni to v0.7.1
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-08-23 20:24:16 +08:00
0d0dcfc02f Cleanup netns after test suit
Signed-off-by: smarkm <smark@freecoop.net>
2019-08-22 08:10:35 +08:00
485be65581 Merge pull request #372 from squeed/fix-build
win-bridge, win-overlay: remove extra import
2019-08-14 11:26:26 -07:00
ca82120019 win-bridge, win-overlay: remove extra import 2019-08-14 18:15:23 +02:00
c9e1c0c1ed Merge pull request #281 from s1061123/dev/support-args
Support "args" in static and tuning
2019-08-14 17:43:04 +02:00
2d6d4b260a Merge pull request #366 from Random-Liu/fix-bridge-race
Fix: failed to set bridge addr: could not add IP address to \"cni0\": file exists
2019-08-12 13:08:32 +02:00
ad7c1d189b Fix a race condition in the bridge plugin.
Signed-off-by: Lantao Liu <lantaol@google.com>
2019-08-09 17:08:04 -07:00
a069a5f1a3 Support "args" in static and tuning
Support "args" field in JSON config to additional configuration
in static and tuning plugins.
2019-08-09 11:52:06 +09:00
ccd683e1a3 Merge pull request #357 from mars1024/bugfix/host-device
host-device: revert name setting to make retries idempotent
2019-08-07 08:54:26 -07:00
a11cb626b0 Merge pull request #331 from nagiesek/LoopbackDsr
Loopback dsr & L2Tunnel
2019-08-07 17:48:00 +02:00
f36dbc2031 Merge pull request #358 from mccv1r0/ipt-vendor
Vendor update go-iptables
2019-08-07 10:40:26 -05:00
e9d511c5bc Merge pull request #364 from s1061123/fix/removeifdown
Remove link Down/Up in MAC address change to prevent route flush
2019-08-07 17:39:02 +02:00
91a68d56f9 Vendor update go-iptables to obtain commit f1d0510cabcb710d5c5dd284096f81444b9d8d10
Update go.mod & go.sub
2019-08-07 10:56:30 -04:00
8902d2614a Remove link Down/Up in MAC address change to prevent route flush 2019-08-07 13:54:10 +09:00
df9af9ab41 [Windows] Adds optional loopbackDSR argument to cni config.
Adds a bool to the cni config that will add a policy that allows for loopbackDSR on an interface. Updates relevant documentation. Allows L2Tunnel networks to be used for L2Bridge plugin.
2019-07-31 15:45:54 -07:00
5e2e365291 host-device: remove useless Expects in testcases
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-07-29 21:09:03 +08:00
4b68f56820 host-device: add testcases for imdempotence of CmdDel
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-07-29 20:47:28 +08:00
ded2f17577 Merge pull request #328 from mars1024/feature/hostlocal_idempotent
host-local support idempotent allocation
2019-07-24 17:32:15 +02:00
57650a1e5b host-device: revert name setting to make retries idempotent
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-07-19 21:26:07 +08:00
7ba2bcfeab Merge pull request #353 from cf-container-networking/fix-hash-func
Increase IfbDeviceName size and refactor hashing functions, fix
2019-07-18 17:12:53 -04:00
3fb8dcfd4c pkg/meta/bandwidth: increase IfbDeviceName size
* Increase entroy from 2 bytes to 7 bytes to prevent collisions
* Extract common library function for hash with prefix
* Refactor portmap plugin to use library function

fixes #347

Co-authored-by: Cameron Moreau <cmoreau@pivotal.io>
Co-authored-by: Mikael Manukyan <mmanukyan@pivotal.io>
2019-07-18 11:45:38 -07:00
966bbcb8a5 Merge pull request #348 from cf-container-networking/update-vagrantfile
update Go version in Vagrantfile
2019-07-10 10:04:52 -05:00
7d76537d4a Merge pull request #349 from cf-container-networking/fix-ip-test
pkg/ip unit test: be agnostic of Linux version
2019-07-10 10:04:32 -05:00
f3b1ffc960 pkg/ip unit test: be agnostic of Linux version
on Linux 4.4 the syscall error message is "invalid argument" not "file
exists"

Co-authored-by: Gabe Rosenhouse <grosenhouse@pivotal.io>
2019-07-09 16:58:57 -07:00
ce9560712e update Go version in Vagrantfile
update go to the lastest version v1.12.7

Co-authored-by: Gabe Rosenhouse <grosenhouse@pivotal.io>
2019-07-09 16:51:00 -07:00
e2984e7840 host-local: return error if duplicate allocation is requested for a given ID
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-07-06 10:05:18 +08:00
eb1ff18c4c host-local: add some testcases for allocation idempotency
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-07-06 09:39:56 +08:00
e8771b36a2 host-local: make allocation idempotent to multiple requests with same id
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-07-06 09:39:56 +08:00
7f8ea631e5 host-local: make Store interface support to get ip list by id
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-07-06 09:39:56 +08:00
0eddc554c0 Merge pull request #343 from s1061123/dev/runtime-ips-mac
Support ips capability in static and mac capability in tuning
2019-07-03 17:48:55 +02:00
e8a25e33cd Merge pull request #344 from cadmuxe/veth_name
Make host-side veth name configurable.
2019-07-03 17:46:02 +02:00
96bd10f679 Add pkg/ip/link_linux.go:SetupVethWithName to support the host-side veth
name configuration.
2019-06-26 10:24:40 -07:00
303299b7b2 Merge pull request #337 from nagiesek/goModUpdate
Go mod update
2019-06-26 11:03:40 -05:00
d42007865a update iptables 2019-06-26 02:14:56 -07:00
ce60e8eb3d dhcp module update 2019-06-26 02:07:24 -07:00
addbcd34b4 update ethtool 2019-06-26 02:07:24 -07:00
e8c953999e vendor update 2019-06-26 02:07:24 -07:00
13fbc4afdf Move over to go mod from dep 2019-06-26 02:07:23 -07:00
545a77f4bb skip makeVeth retry if a vethPeerName is set. 2019-06-25 14:17:42 -07:00
c204dbd47c update pkg/ip/link_linux.go:SetupVeth to support host-side veth name config. 2019-06-21 11:39:30 -07:00
660685a8af Support ips capability in static and mac capability in tuning
This change introduces new capability flag to change MAC address
and to specify IP addresses by tuning and static.
2019-06-20 17:11:47 +09:00
2b6808807f Merge pull request #341 from mars1024/bump_cni_to_0.7.1
bump containernetworking/cni to v0.7.1
2019-06-19 17:51:14 +02:00
869d5ec873 pkg/ipam : use delegateArgs instead of env set/unset in ipam.ExecDel
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-06-18 21:02:01 +08:00
93919752fb bump containernetworking/cni up to v0.7.1
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-06-18 20:49:51 +08:00
fe60fcddb8 Merge pull request #326 from squeed/bump-release-go
Release: bump go to v1.12
2019-05-29 11:30:15 -05:00
e308211d34 Merge pull request #300 from zshi-redhat/host-device-pci-parameter
host-device: add pciBusID property
2019-05-29 11:06:00 -05:00
29a431f1fc Release: bump go to v1.12 2019-05-29 17:58:47 +02:00
7d75ab66a4 Merge pull request #321 from mars1024/modify/bandwidth
bandwidth: get bandwidth interface in host ns through container interface
2019-05-29 17:57:36 +02:00
d5c8b4b206 Merge pull request #325 from nickethier/b-bridge-ipmasq
bridge: fix ipMasq setup to use correct source address
2019-05-29 16:44:18 +01:00
5a4085f1fa Merge pull request #324 from moshe010/fix_386
fix compilation error on 386
2019-05-29 10:43:18 -05:00
37d2ee1d5d bridge: don't use canonical CIDR address when setting up IP masquerade 2019-05-25 20:55:47 -04:00
7f9b1844b8 bridge: add test for ipMasq rules 2019-05-24 23:14:31 -04:00
2753b9af8f fix compilation error on 386
Update github.com/safchain/ethtool to fix the compilation
error on 386. Also added 386 to the tarvis yaml.

Fixes #322

Signed-off-by: Moshe Levi <moshele@mellanox.com>
2019-05-24 23:43:37 +03:00
d35c96dda6 bandwidth: add testcases for func getHostInterface
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-05-21 00:28:04 +08:00
344d343431 bandwidth: get bandwidth interface in host ns through container interface
Signed-off-by: Bruce Ma <brucema19901024@gmail.com>
2019-05-18 14:06:45 +08:00
0950a3607b Merge pull request #279 from mars1024/bugfix/ipmasq_source
change source of ipmasq rule from ipn to ip
2019-05-10 13:40:17 +02:00
a6a8a818e4 Merge pull request #314 from mccv1r0/ipam303
Have ipam.ExecDel set CNI_COMMAND to DEL
2019-05-08 11:09:39 -05:00
02ccf1ef9b Have ipam.ExecDel set CNI_COMMAND to DEL 2019-05-08 11:50:28 -04:00
a03e6b505e Merge pull request #307 from SchSeba/l2-macvlan
Allow to configure empty ipam for macvlan
2019-05-08 17:27:52 +02:00
6bceb68143 Merge pull request #309 from nagiesek/flannel
Flannel: Pass through runtimeConfig to delegate
2019-05-08 10:25:50 -05:00
e2187efec1 Merge pull request #312 from nagiesek/caseInsensitiveCompare
Windows: case insensitive compare for ids
2019-05-08 10:25:28 -05:00
5a78120e24 Windows: case insensitive compare for ids 2019-05-02 19:54:39 -07:00
071907f867 Flannel: pass runtimeConfig into delegate 2019-05-01 15:58:24 -07:00
2d6d46d308 Merge pull request #311 from dcbw/get-to-check-cleanups
windows,flannel: cmdGet -> cmdCheck and fix flannel Check return
2019-05-01 14:17:48 -05:00
af692de1b8 Allow to configure empty ipam for macvlan
This PR add the option to configure an empty ipam for the macvlan cni plugin.

When using the macvlan cni plugin with an empty ipam the requeted pod will get the macvlan interface but without any ip address.

One of the use cases for this feature is for projects that runs a dhcp server inside the pod like KubeVirt.
In KubeVirt we need to let the vm running inside the pod to make the dhcp request so it will be able to make a release an renew request when needed.
2019-05-01 20:29:22 +03:00
ae0b03787f windows,flannel: cmdGet -> cmdCheck and fix flannel Check return 2019-05-01 10:48:14 -05:00
b199dc3ffe Merge pull request #310 from squeed/cni-07
vendor: bump cni to v0.7.0
2019-04-30 09:38:20 -05:00
dbc86e4db2 Merge pull request #298 from s1061123/dev/macvlan-default-master
macvlan: make master config as optional
2019-04-30 15:47:51 +02:00
29801c45a7 vendor: bump cni to v0.7.0 2019-04-30 15:46:12 +02:00
12b426a921 update go-iptables to v0.4.1 to fix CI broken 2019-04-26 20:34:46 +08:00
7efec9ea46 change source of ipmasq rule from ipn to ip 2019-04-25 15:59:02 +08:00
79b1c402c4 host-device: add deviceID property
Fixes #253
2019-04-25 12:17:40 +08:00
44297f6ba3 Merge pull request #304 from mccv1r0/sbrnoop
Don't return error for cmdCheck() when support is not implemented
2019-04-24 15:30:59 -05:00
cc4976a43c return nil instead of error for cmdCheck until support is added 2019-04-24 13:47:58 -04:00
827a4bd843 Merge pull request #301 from mars1024/bugfix/ipam_revert
fix bug on ip revert if cmdAdd fails on macvlan and host-device
2019-04-24 10:36:40 -05:00
697550083d Merge pull request #286 from nagiesek/noErrorEndpointNotFound
Windows: No error for no endpoint on DEL. Ipam clean-up on ADD failure
2019-04-24 10:36:25 -05:00
214bcb8c8b fix bug on ipam revert if cmdAdd fails on macvlan and host-device 2019-04-18 21:16:29 +08:00
635968aaff Merge branch 'master' into noErrorEndpointNotFound 2019-04-17 23:04:04 -07:00
7df5acee0f Merge pull request #288 from dcbw/coveralls
test: add coveralls support
2019-04-17 13:54:51 -05:00
7924ef76da Incorporate with reviewer's comments 2019-04-18 00:54:31 +09:00
5e25b0e40c Merge pull request #295 from squeed/buildversion
plugins: correctly output build version, cosmetic cleanups
2019-04-17 17:54:05 +02:00
688a87a055 Windows: No error for no endpoint found On Del. Ipam clean-up on ADD failure.
We used to return error if no endpoint was found during delete. We now treat this as a success. If we fail during an add call, we now make a delete delegate call to the ipam to clean-up.
2019-04-17 08:33:10 -07:00
229ec3602d test: add coveralls support 2019-04-17 10:30:11 -05:00
8c52f50849 macvlan: make master config as optional
To ease of config, make master config as optional parameter.
In default case, default route interface will be used for master.
2019-04-17 16:28:27 +09:00
6ea047fe09 build_linux: don't build the sample plugin
It shouldn't be included in releases.
2019-04-15 16:52:11 +02:00
72f2a1ffd4 plugins: correctly output build version, cosmetic cleanups
Now that libcni has the ability to print a version message, plumb it
through correctly.

While we're at it,
- fix import paths
- run gofmt
- add some more comments to sample
- add container runtime swappability for release
2019-04-15 16:52:07 +02:00
6733d30762 Merge pull request #290 from mccv1r0/pr75
PR75 updated with Check Support
2019-04-12 14:15:10 -05:00
d47387c6fe Add Check support to firewall meta plugin, test cases 2019-04-12 14:37:21 -04:00
95be5da5e2 firewall: add a couple more testcases 2019-04-12 14:37:21 -04:00
b46e1a0138 firewall: consolidate firewalld code into firewall plugin 2019-04-12 14:37:21 -04:00
9d6f1e9975 firewall: add firewalld functionality to firewall plugin
Example of usage, which uses flannel for allocating IP
addresses for containers and then registers them in `trusted`
zone in firewalld:

{
  "cniVersion": "0.3.1",
  "name": "flannel-firewalld",
  "plugins": [
    {
      "name": "cbr0",
      "type": "flannel",
      "delegate": {
        "isDefaultGateway": true
      }
    },
    {
      "type": "firewall",
      "backend": "firewalld",
      "zone": "trusted"
    }
  ]
}

Fixes #114

Signed-off-by: Alban Crequy <alban@kinvolk.io>
Signed-off-by: Michal Rostecki <mrostecki@suse.com>
2019-04-12 14:37:21 -04:00
eb66fc201c vendor: Add github.com/godbus/dbus
Signed-off-by: Michal Rostecki <mrostecki@suse.com>
2019-04-12 14:37:21 -04:00
d096a4df48 firewall: new plugin which allows a host interface to send/receive traffic
Distros often have additional rules in the their iptabvles 'filter' table
that do things like:

-A FORWARD -j REJECT --reject-with icmp-host-prohibited

docker, for example, gets around this by adding explicit rules to the filter
table's FORWARD chain to allow traffic from the docker0 interface.  Do that
for a given host interface too, as a chained plugin.
2019-04-12 14:37:21 -04:00
e9e1d37309 Merge pull request #231 from SchSeba/add-vlan-tag-to-bridge
Added vlan tag to the bridge cni plugin.
2019-04-11 11:58:33 -05:00
b69a2dd3d7 Added vlan tag to the bridge cni plugin.
With the VLAN filter, the Linux bridge acts more like a real switch, Allow to tag and untag
vlan id's on every interface connected to the bridge.

This PR also creates a veth interface for the bridge vlan interface on L3 configuration.

Related to https://developers.redhat.com/blog/2017/09/14/vlan-filter-support-on-bridge/ post.

Note: This feature was introduced in Linux kernel 3.8 and was added to RHEL in version 7.0.
2019-04-10 21:59:52 +03:00
0d6198bac4 Merge pull request #292 from squeed/cni-07
vendor: bump libcni to v0.7.0-rc2
2019-04-10 16:50:25 +01:00
914f2bc740 set CGO_ENABLED=0 in travis.yml 2019-04-10 17:24:42 +02:00
e028b70b49 vendor: bump libcni to v0.7.0-rc2 2019-04-10 17:16:27 +02:00
fbd9accd14 Merge pull request #268 from s1061123/fix/dhcp_daemon_hostprefix
Fix -hostprefix option
2019-04-08 12:53:09 +02:00
8b53f80fa8 Incorporate with @bboreham comment 2019-04-05 03:38:20 +09:00
dc536993e2 vendor folder bump. 2019-04-04 16:39:05 +03:00
18874aac7d Merge pull request #264 from mccv1r0/add-check
Add CHECK support for linux plugins
2019-04-03 12:58:41 -05:00
74a2596573 Add check support for: bridge, ipvlan, macvlan, p2p, vlan and host-device main plugins
host-local and static ipam plugins
  tuning, bandwidth and portmap meta plugins

  Utility functions created for common PrevResult checking

  Fix windows build
2019-04-03 13:08:07 -04:00
8a3d149a60 Merge pull request #284 from francares/fmc_dhcp_subnetmask
IPAM/DHCP: include Subnet Mask option parameter in DHCPREQUEST
2019-04-01 14:56:13 -05:00
9c9a8e991d Merge pull request #271 from nagiesek/dnsRuntimeConfigWindows
Windows: Add runtime DNS and del bug fix
2019-03-27 10:58:05 -05:00
f55d8d02ce Merge pull request #276 from s1061123/dev/add-dhcp-systemd-file
Add systemd unit file to activate socket with systemd
2019-03-27 09:23:15 -05:00
d577d9cc24 DHCP: include Subnet Mask option parameter in DHCPREQUEST
DHCP REQUEST from DHCP plugin does not include Subnet Mask option parameter (1). Some DHCP servers need that option to be explicit in order to return it in a DHCPACK message.
If not, DHCP plugin returns "DHCP option Subnet Mask not found in DHCPACK" error msg in this type of scenario.
2019-03-26 12:49:48 -07:00
4ec62ac295 Fix -hostprefix option
Fix #267
2019-03-26 18:27:46 +09:00
82a0651d0a Merge pull request #278 from dcbw/portmap-prepend
Portmap: append, rather than prepend, entry rules
2019-03-20 10:21:01 -05:00
9fab520c37 Portmap: append, rather than prepend, entry rules
This means that portmapped connections can be more easily controlled /
firewalled.
2019-03-18 14:03:13 -05:00
b1814d7f9a Add systemd unit file to activate socket with systemd
This changes to add sample systemd unit files to activate socket
with systemd. Fix #156.
2019-03-14 16:03:23 +09:00
b71e8db683 Windows: Add runtime DNS and del bug fix
This adds the dns capability for supplying a runtime dnsConfig from a CRI. It also includes a bug fix for removing an endpoint when no IPAM is supplied. Adds version dependency of 0.3.0. Mild updates to windows READMEs.
2019-03-12 20:56:11 -07:00
afd7391938 Merge pull request #266 from huynq0911/fix_redundant_import_alias
Fix redundant import alias
2019-02-27 14:12:22 -06:00
a95d48bb01 Merge pull request #215 from nagiesek/addHcnSupport
Windows: Adds support for Host Compute Calls for Win-Bridge
2019-02-27 14:11:45 -06:00
57b42a7b99 Windows: Adds HCS Calls and Bug Fixes
Move the windows plugin to use the Host Compute (v2) APIs, as well
as clean-up the code. Allows win-bridge to use either the old API or Host Compute (v2) api
depending on a conf parameter. Fixes a leaked endpoint issue on windows for the v1 flow, and
removes the hns/pkg from the linux test run.
2019-02-27 08:49:34 -08:00
4bca3e76bf Fix redundant import alias
Because these package names are short and they are unique, it can not conflict. So the import aliases that can be omitted and we should remove them.
2019-02-20 16:58:45 +07:00
1865a0701e Merge pull request #212 from plwhite/sbrplugin
Create new Source Based Routing plugin
2019-02-06 10:15:56 -06:00
29928cff4d Create new Source Based Routing plugin
This creates a new plugin (sbr) which sets up source based routing, for use
as a chained plugin for multi-network environments.
2019-01-31 09:27:59 +00:00
c74e0e9967 Merge pull request #255 from saravanakumar-periyasamy/master
some DHCP servers expect to request for explicit router options
2019-01-30 10:07:20 -06:00
9226c9642d Merge pull request #254 from daschott/master
Added CONTRIBUTORS.md for Windows and CNI plugins OWNERS.md
2019-01-30 10:06:50 -06:00
fd71dae5a7 Merge pull request #256 from s1061123/fix/host-device-readme
Fix host-device README.md
2019-01-30 10:06:01 -06:00
d29d56bc4d Fix host-device README.md
host-device's README.md missing 'type' field, so this change
just adds 'type' in config example.
2019-01-30 01:51:57 +09:00
f891fd9e9a some DHCP servers expect to request for explicit router options 2019-01-28 19:44:53 -06:00
a5378f2ccf added CONTRIBUTORS.md for Windows and CNI plugins OWNERS.md 2019-01-25 13:25:56 -08:00
9a429d8d25 Windows: Updates Windows Vendoring
Updates windows dependent libraries for vendoing.
2019-01-23 18:43:18 -08:00
a686cc4bd8 Merge pull request #248 from JoeWrightss/patch-1
Fix some spelling errors
2019-01-09 09:50:38 -06:00
f5c451f719 remove changing
Signed-off-by: JoeWrightss <zhoulin.xie@daocloud.io>
2019-01-04 01:14:56 +08:00
f0208611ad Fix some spelling errors
Signed-off-by: JoeWrightss <zhoulin.xie@daocloud.io>
2018-12-29 21:26:28 +08:00
ee819c71a1 Merge pull request #246 from pivotal-k8s/windows-travis
Move Windows tests to Travis
2018-12-19 10:38:24 -06:00
4371665606 Merge pull request #236 from BSWANG/master
remove gateway check
2018-12-12 18:41:02 +01:00
d0a28ce5f7 Merge pull request #239 from sufuf3/fix-typo
Fix typo from userful to useful in README
2018-12-12 08:14:24 -08:00
34b1b702dc Move Windows tests to Travis 2018-12-10 14:43:32 -08:00
35451e67e2 Fix typo from userful to useful in README 2018-12-03 14:35:19 +08:00
59a746bd52 remove gateway check
Signed-off-by: bingshen.wbs <bingshen.wbs@alibaba-inc.com>
2018-11-29 12:59:38 +08:00
3fb464786f Merge pull request #195 from SchSeba/l2-bridge
L2 bridge Implementation
2018-11-21 08:41:24 -08:00
72251a67b5 Merge pull request #217 from mccv1r0/dhcpif
Add container ifName to the dhcp clientID, making the clientID value
2018-11-21 10:34:10 -06:00
b75d14ab84 Merge pull request #234 from plwhite/vagrant
Tweak contributing instructions to clarify how to run tests.
2018-11-21 10:22:16 -06:00
bf47e9aa1b Allow L2 connection for the bridge cni plugin. 2018-11-21 17:57:10 +02:00
251a00e6b4 Tweak contributing instructions to clarify how to run tests. 2018-11-19 14:39:39 +00:00
0af31fc4d0 Change dhcp plugin to send ClientID allowing container to have multiple CNI
interfaces using dhcp ipam.

Vendor latest dhcp4server, dhcp4client, dhcp4

Added additional tests for new functionality in dhcp2_test.go

Wrap d2g dhcp4client calls with our own which add clientID to packet.
2018-11-15 11:31:56 -05:00
227a4c15fa Add container ifName to the dhcp clientID, making the clientID value
now equal to: ContainerID / Host Network / ifname inside container

Use clientID as a key for the lease
2018-11-09 17:27:05 -05:00
073df9a04d Merge pull request #219 from pivotal-k8s/master
Make build.sh work for windows plugins.
2018-11-07 08:25:25 -08:00
71e026426f Merge pull request #227 from mccv1r0/check-vendor
Vendor github.com/containernetworking/cni libcni and pkg
2018-11-07 16:14:39 +00:00
5fc8209482 Merge pull request #218 from hwchiu/fix-readme
fix the typo of macvlan and also modify documents to meet the current plugins.
2018-11-07 09:30:29 -06:00
534bfafe99 Merge pull request #220 from hwchiu/host-device-support-ipam
Support the IPAM in the host-device
2018-11-07 09:29:58 -06:00
44757b95ef Merge pull request #223 from rosenhouse/bump-to-go-1.11
bump travis and vagrantfile to go 1.11
2018-11-07 09:28:42 -06:00
6f3332e9fe Fix for windows too 2018-11-05 16:52:12 -05:00
ddbf22f7f9 Vendor github.com/containernetworking/cni libcni and pkg file needed for CHECK
Update plugins/tests to deal with changes made to this vendor'ed code
2018-11-05 16:35:03 -05:00
4e1f7802db Split build.sh into two OS-specific scripts 2018-11-01 16:41:31 -04:00
d3284f7c5e Support the IPAM for host-device plugin
- Change variable name to camel style to fix golint warning
- Execute the IPAM to assign the IP address if it's inside in the config
- Test the IPAM module with static plugin
2018-11-01 01:12:47 +00:00
ee57f675ad bump travis and vagrantfile to go 1.11 2018-10-31 08:53:28 -07:00
f006155b66 Add the missing documents for some existing plugins 2018-10-18 02:20:52 +00:00
2d03820ebb Make build.sh work for windows plugins.
When building the windows plugin exe's (host-local, flannel, win-overlay, win-bridge),
it was necessary to use 'GOOS=windows go build path/to/plugin' rather than the build script.

This makes 'GOOS=windows GOARCH=amd64 ./build.sh' build all the windows plugin binaries.
2018-10-17 16:44:57 -07:00
e3ec4a74d0 fix the typo, we use the passthru in the macvlan.go 2018-10-17 17:15:43 +00:00
b93d284d18 Merge pull request #213 from Random-Liu/handle-empty-netns
Handle empty netns in DEL for `loopback` and `host-device`.
2018-10-17 10:21:29 -05:00
3129152706 Merge pull request #216 from plwhite/dco
Add missing DCO
2018-10-17 10:05:45 -05:00
9abd20edd6 Add missing DCO
DCO referenced from CONTRIBUTING.md did not exist. Add it.
2018-10-11 16:15:24 +01:00
2955d63c95 Handle empty netns in DEL for loopback and host-device.
Signed-off-by: Lantao Liu <lantaol@google.com>
2018-10-10 10:49:06 -07:00
a326f9d3f8 Merge pull request #203 from mccv1r0/issue164
host-local: add ifname to file tracking IP address used
2018-10-10 11:55:47 -05:00
3072177d0e Merge branch 'issue164' of github.com:mccv1r0/plugins into issue164 2018-10-10 12:32:34 -04:00
d2ebd0a4c2 Merge branch 'master' of github.com:containernetworking/plugins into issue164 2018-10-10 12:28:43 -04:00
f879dad626 Merge pull request #207 from mccv1r0/vendorfix
Fix vendor regression in dhcp4server
2018-10-10 10:50:53 -05:00
22b11bb367 Keep defaultSocketPath the same as before 2018-10-10 11:35:57 -04:00
396f51afab Merge branch 'vendorfix' of github.com:mccv1r0/plugins into vendorfix 2018-10-09 16:59:01 -04:00
73106f0ece Remove unused pidfilePath const
Moved global var's to BeforeEach()
2018-10-09 16:57:58 -04:00
bf31f08981 Remove unused pidfilePath const 2018-10-09 16:39:06 -04:00
75e35b6c01 Added new test cases.
Add two interfaces (e.g. eth0, eth1) to the same container.
Ensure each file now has ContainerID and ifname.
Delete one, ensure that the right file was deleted.

Add an interface using just ContainerID in the file.
Delete to verify we are still backwards compatible with any
files created using earlier verison of host-local plugin.
2018-10-09 13:13:36 -04:00
37715a0f87 Moved directory walk, compare file code to it's own function 2018-10-09 13:13:36 -04:00
a17cadda88 Handle the case of a Delete for a reservation stored with just ContainerID without the interface 2018-10-09 13:13:36 -04:00
1e8f9525a6 Obtain ifname from CmdArgs and pass to backend Add ifname to second line of file tracking the IP address used by ContainerID
Update host-local tests to use ifname along with ContainerID
in store file

Signed-off-by: Michael Cambria <mcambria@redhat.com>
2018-10-09 13:13:36 -04:00
ef913eadd5 Took out os.Remove() for socketPath and pidfilePath now that os.RemoveAll() is used 2018-10-08 16:01:31 -04:00
3d349e4645 Ensure /run/cni exists in the filesystem before running dhcp tests 2018-10-08 14:08:37 -04:00
3de323f3f0 Enable ginkgo parallelization in test.sh 2018-10-08 11:32:08 -04:00
5fd849ac6d Use tempDir in socket path for ginkgo parallelization 2018-10-08 11:31:03 -04:00
6d3215a256 Allow socket path used by dhcp plugin to be supplied via dhcp ipam configuration
Allow socket path to be supplied as flag when starting dhcp daemon
2018-10-08 11:30:01 -04:00
726759b29b Merge pull request #209 from angelachin/readme-bandwidth-update
Updated README to include bandwidth plugin
2018-10-05 10:02:21 -07:00
66837d6f3b Remove -p from test.sh
Running ginkgo tests in parallel causes problems with dhcp_test.go.

BeforeEach() is run once for each spec before any actual dhcp test starts.
This results in setting up two dhcp4servers that run concurrently.
Both try to Listen and use unix socketPath file /run/cni/dhcp.sock at the same time.

AfterEach() for one test runs when test completes, deleting /run/cni/dhcp.sock.
But other test still needs the file resulting in test failing.  Often, the next dhcp
test hasn't started yet.  When test does start it waits 15 seconds for dhcp4server to
create /run/cni/dhcp.sock (which has just been deleted) so test fails.

Other times dhcp tests fail because /run/cni/dhcp.sock is deleted while still being used.
2018-10-01 16:12:07 -04:00
1e4d47fc35 Fix vendor regression in dhcp4server 2018-09-27 11:04:14 -04:00
0fbc611121 Added new test cases.
Add two interfaces (e.g. eth0, eth1) to the same container.
Ensure each file now has ContainerID and ifname.
Delete one, ensure that the right file was deleted.

Add an interface using just ContainerID in the file.
Delete to verify we are still backwards compatible with any
files created using earlier verison of host-local plugin.
2018-09-25 16:17:18 -04:00
9959f787e8 Moved directory walk, compare file code to it's own function 2018-09-25 12:10:36 -04:00
646dbbace1 Merge pull request #165 from s1061123/dev/static-args
Support CNI_ARGS in static IPAM plugin
2018-09-24 21:00:09 -05:00
9b86f52791 Merge pull request #200 from s1061123/fix/tuning-doc
Add description for mac/mtu/promisc in tuning README.md
2018-09-24 20:53:38 -05:00
8a579a7fbc Merge pull request #204 from mrostecki/always-check-err
Add missing error checks
2018-09-24 20:52:38 -05:00
a8ad12dd7a Merge pull request #193 from thxCode/windows_cni
Windows CNI support
2018-09-20 15:16:52 -05:00
b56ca2fe45 Windows Support
Patch for https://github.com/containernetworking/plugins/pull/85

+ Windows cni plugins are added
   (*) win-bridge (hostgw)
   (*) win-overlay (vxlan)
+ Windows netconf unit test
+ Fix appveyor config to run the test
+ Build release support for windows plugins

Address comments

From:
    - https://github.com/containernetworking/plugins/pull/85
    - 0049c64e3f
2018-09-21 00:34:07 +08:00
e1d29e9fe4 Update Vendor
(*) github.com/Microsoft/hcsshim
    (*) golang.org/x/sys
    (*) github.com/x/cyrpto
    (*) github.com/sirupsen/logrus
    (*) github.com/Microsoft/go-winio
    (*) github.com/juju/errors
    (*) github.com/buger/jsonparser
2018-09-21 00:34:07 +08:00
7b72f2afd4 Handle the case of a Delete for a reservation stored with just ContainerID without the interface 2018-09-20 10:56:07 -04:00
93178bf026 Fix typo. 2018-09-20 22:18:15 +09:00
f5f787057d plugins/meta: Add missing error checks
Signed-off-by: Michal Rostecki <mrostecki@suse.de>
2018-09-20 11:06:50 +02:00
8c61a2c2f3 plugins/main: Add missing error checks
Signed-off-by: Michal Rostecki <mrostecki@suse.de>
2018-09-20 11:06:28 +02:00
1a3f49c7ae plugins/ipam: Add missing error check
Signed-off-by: Michal Rostecki <mrostecki@suse.de>
2018-09-20 11:05:58 +02:00
947a5881fc pkg/ns: Add missing error checks
Signed-off-by: Michal Rostecki <mrostecki@suse.de>
2018-09-20 11:05:15 +02:00
26834c3e63 pkg/ip: Add missing error check
Signed-off-by: Michal Rostecki <mrostecki@suse.de>
2018-09-20 11:04:13 +02:00
f1ee2cc614 Obtain ifname from CmdArgs and pass to backend Add ifname to second line of file tracking the IP address used by ContainerID
Update host-local tests to use ifname along with ContainerID
in store file

Signed-off-by: Michael Cambria <mcambria@redhat.com>
2018-09-18 16:35:30 -04:00
d22e75316f Incorporate comments in PR. 2018-09-13 01:49:47 +09:00
094c903932 Incorporate Dan's comments. 2018-09-13 01:46:11 +09:00
61a412ea77 Align test suite name with others 2018-09-06 15:56:40 +09:00
61c136126f Support multiple IP addresses in CNI_ARGS 2018-09-06 15:50:18 +09:00
35b87a34db Merge pull request #191 from dcbw/portmap-panic-fix
portmap: don't panic if listing a chain returns fewer lines than exected
2018-09-05 10:09:02 -05:00
9048a61dda Add description for mac/mtu/promisc in tuning README.md
This diff adds documents for #177 change (mac/mtu/promisc) in tuning
README.md. Fixes #199.
2018-09-05 16:01:24 +09:00
321467bf1b Merge remote-tracking branch 'upstream/master' into dev/static-args 2018-09-05 14:39:16 +09:00
60a99ca331 Incorporate Dan's comments. 2018-09-05 14:37:31 +09:00
7d329215b0 portmap: don't panic if listing a chain returns fewer lines than expected 2018-08-16 09:50:28 -05:00
6a12a375cd Merge pull request #190 from dongjun666/bandwidth_desc
Correct the bandwidth unit in description
2018-08-15 17:00:24 +01:00
220499db6b Correct the bandwidth unit in description
Replace Kbps with bps and Kb with bits in bandwidth description.
2018-08-15 19:26:13 +08:00
5532950dce Merge pull request #186 from ncdc/fix-release-no-tty
scripts: support building releases without a TTY
2018-08-14 13:22:27 -05:00
fb7a24405e Merge pull request #177 from s1061123/tuninig-iplink
tuning: add some generic link settings (MAC, promisc, MTU)
2018-08-14 13:22:10 -05:00
9689522b4f scripts: support building releases without a TTY
This allows systems such as Jenkins, which do not provide a TTY, to run
scripts/release.sh

Signed-off-by: Andy Goldstein <andy.goldstein@gmail.com>
2018-08-09 10:20:40 -04:00
9425d24c28 Incorporate @jelloneck/@bboreham/@squeed's comments. 2018-08-09 22:28:12 +09:00
635cb22f12 Merge remote-tracking branch 'origin/master' into tuninig-iplink 2018-08-09 21:43:33 +09:00
75d3585862 Merge pull request #174 from jellonek/updatecomment
plugins/ipam/static: Update docstring
2018-08-01 16:28:45 +01:00
3a7f254a63 Introduce iplink(MTU MAC and promiscas) feature into tuning
This change adds 'ip link' command related feature into tuning
meta cni plugin. Currently MTU, MAC and promiscas mode are
supported.
2018-07-31 12:54:15 +09:00
2b819b5571 Merge pull request #129 from mauriciovasquezbernal/fix_ip_leakage_bridge
bridge: release IP in case of error
2018-07-27 15:58:08 -05:00
316489903b bridge: add test case for release IP on error
Signed-off-by: Mauricio Vasquez B <mauricio.vasquez@polito.it>
2018-07-27 07:57:12 -05:00
3a7ee332be bridge: release IP in case of error
If there is an error after an IP has been allocated it is necesary
to release it.

Signed-off-by: Mauricio Vasquez B <mauricio.vasquez@polito.it>
2018-07-27 07:57:04 -05:00
cc3ad26691 Incorporate Casey's comments 2018-07-27 12:47:28 +09:00
4b296ba330 bridge: add random datadir to all testcases 2018-07-26 21:09:30 -05:00
1562a1e60e Merge pull request #171 from containernetworking/build-instructions
Add build instructions
2018-07-25 16:30:56 +01:00
6dc16b9132 plugins/ipam/static: Update docstring 2018-07-19 14:35:10 +02:00
21e3e4ec01 Merge pull request #175 from liucimin/fix_loopback_readme
fix_loopback_readme
2018-07-18 09:02:48 -05:00
427a38e2d7 fix_loopback_readme 2018-07-11 23:59:09 +08:00
f970542036 Merge pull request #173 from sak0/pr
remove duplicated assginment.
2018-07-11 16:32:59 +01:00
9201a5a433 Merge pull request #169 from hustcat/bandwidth-burst-fix
Fix tc-tbf burst value in bytes
2018-07-11 16:31:53 +01:00
3b3622db67 Incorporate jellonek's comments. 2018-07-06 13:17:22 +09:00
dc899ac0e0 remove duplicated assginment.
Signed-off-by: CuiHaozhi <61755280@qq.com>
2018-07-05 09:21:09 +08:00
1f77018b60 Add build instructions
Signed-off-by: Bryan Boreham <bjboreham@gmail.com>
2018-07-04 14:41:16 +00:00
7571169160 Fix tc-tbf burst value in bytes. 2018-07-03 18:36:49 +08:00
696b1f9ab1 Merge pull request #166 from NeilW/host-loca
plugins/host-local: ensure subnet is a network address
2018-06-20 17:49:42 +02:00
b2fc336833 plugins/host-local: ensure subnet is a network address
Allocation code assumes the specified subnet is a clean network address
prefix, so check that is the case and throw an error otherwise

Fixes #161
2018-06-18 10:13:34 +01:00
6da1cb7876 Support CNI_ARGS in static IPAM plugin
This change is to add CNI_ARGS support in static IPAM plugin.
When IP/SUBNET/GATEWAY are given in CNI_ARGS, static IPAM adds
these info in addition to config files.

To configure ip address only from CNI_ARGS, 'address' field in config
is changed to optional from required.
2018-06-18 16:18:47 +09:00
1f6e6ef198 Merge pull request #158 from squeed/bump-skel
vendor: update cni to v0.7.0-alpha1
2018-06-15 19:00:09 +02:00
68b4efb405 plugins/* stub-out GET functions so plugins build with v0.7 2018-06-15 15:28:53 +02:00
e4fdb6cd18 vendor: bump cni to v0.7.0-alpha0.
This will break building, as the plugins will need to be updated for
the new signatures.
2018-06-13 17:14:35 +02:00
2b8b1ac0af Merge pull request #154 from lucab/ups/bump-go-systemd
vendor: bump go-systemd to v17
2018-06-06 16:10:04 +01:00
475fdb8a0a plugins/dhcp: update to new go-systemd 2018-05-28 12:35:04 +00:00
50d626fe02 vendor: bump go-systemd to v17 2018-05-28 12:33:21 +00:00
1d973f59d2 Merge pull request #147 from databus23/host-device-fix
host-device: Ensure device is down before rename
2018-05-16 10:54:47 -05:00
79e7b27494 Updated README to include bandwidth plugin
Also standardized to upper case for sentence starts.
2018-05-15 18:15:32 +01:00
dfbaeccc12 Merge pull request #148 from dcbw/tests-pass-containerid
plugins/testutils: pass CNI_CONTAINERID to plugins in testcases
2018-05-09 12:50:14 -05:00
731298003c plugins/testutils: pass CNI_CONTAINERID to plugins in testcases
Recent CNI specification changes require the container ID on ADD/DEL,
which the testcases were not providing.  Fix that up so things work
when this repo gets CNI revendored.
2018-04-26 11:24:30 -05:00
eb410208cb host-device: Ensure device is down before rename
If the device is in state up trying to set the name fails with "device or resource busy"
2018-04-25 20:39:23 +02:00
1df359a210 Merge pull request #144 from squeed/build-fixes
build: some small improvements; bump to go1.10
2018-04-25 10:41:32 -05:00
adba9ec16e Merge pull request #138 from m1093782566/runtime-config
traffic shaping: take configuration via a runtimeConfig
2018-04-25 17:09:41 +02:00
4a0971bcd8 update integration test configlist 2018-04-19 10:08:25 +08:00
7cf02869ec traffic shaping: take configuration via a runtimeConfig 2018-04-13 10:57:17 +08:00
aade7b93ee build: some small improvements; bump to go1.10
- bump to go 1.10
- Add a linker tag with the build version
- Remove fastbuild, go builds are cached now
- Use better ginkgo suite names
2018-04-12 16:59:51 +02:00
bb1e8d10a7 Merge pull request #143 from containernetworking/v0.7
Merge v0.7.1 fix into master
2018-04-12 09:21:42 -05:00
0dba9daee0 Merge pull request #136 from s1061123/add_static
plugins: add static CNI plugin
2018-04-12 06:34:52 -05:00
72b62babee Merge pull request #141 from squeed/missing-mac
pkg/ip: re-fetch the created link to return creation-time parameters
2018-04-12 11:09:35 +02:00
e4f13535ab Merge pull request #131 from squeed/no-delete-ns
pkg/ns: remove namespace creation (and move to testutils)
2018-04-11 13:32:32 -05:00
26dafaa097 plugins/ptp: test for valid data in Interfaces field 2018-04-11 15:19:20 +02:00
13e6a4b2ba plugins/bridge: Make stricter assertions about the return data 2018-04-11 15:10:39 +02:00
00b072dd0b pkg/ip: re-fetch the created link to return creation-time parameters
Fixes: #140
2018-04-11 15:04:38 +02:00
a0eac8d7d9 pkg/ns: remove namespace creation (and move to testutils)
Namespace creation had an unergonomic interface and isn't used, except
for testing code. Remove it; downstream users should really be creating
their own namespaces
2018-04-03 18:56:25 +02:00
263ff063a6 Add static CNI plugin
This changes to add 'static' CNI plugin, which provides to assign
IPv4/v6 address statically from given config file. See README.md
for the details.
2018-03-29 14:10:04 +09:00
9c36007eea Merge pull request #132 from lsm5/custom-go
allow building with custom Go binary
2018-03-28 10:10:44 -05:00
9bdf37ff9a allow building with custom Go binary
I need to build deb packages using golang-1.10 on Ubuntu Xenial which is
present in a non-default location while /usr/bin/go is at golang-1.6.

With this commit I can simply run:
`GO=/usr/lib/go-1.10/bin/go ./build.sh`
to use golang-1.10.

Signed-off-by: Lokesh Mandvekar <lsm5@fedoraproject.org>
2018-03-23 13:55:30 +00:00
e590fca5ae Merge pull request #130 from jingax10/fix_typo_branch
Fix a typo in PTP plugin comment, i.e., s/IFF_POINTOPONT/IFF_POINTTOPOINT.
2018-03-21 08:28:55 -05:00
00fbba085e Fix a typo s/IFF_POINTOPONT/IFF_POINTTOPOINT. 2018-03-15 22:47:54 -07:00
1fb94a4222 Merge pull request #96 from DennisDenuto/denuto/master
plugins/meta/bandwidth: traffic shaping plugin
2018-03-14 08:11:28 -07:00
fe0cf201f8 Safely print error
Format plugin code

Signed-off-by: Aidan Obley <aobley@pivotal.io>
2018-03-12 15:53:23 -07:00
d2f6472474 Ensure the bandwith plugin chooses the host veth device
When chained with a plugin that returns multiple devices, the bandwidth
plugin chooses the host veth device.

Signed-off-by: Tyler Schultz <tschultz@pivotal.io>
2018-03-12 15:08:53 -07:00
56989e2380 Merge pull request #124 from squeed/masq-del
pkg/ip: Tearing down ipmasq should be idempotent
2018-03-07 17:40:21 +01:00
d5bdfe4cbd top-level integration test for bridge + bandwidth 2018-02-28 13:28:06 -08:00
2793dd11cb top-level integration test coverage for ptp + bandwidth 2018-02-28 12:56:10 -08:00
90252c30fb meta/bandwidth: package main so we can build a binary 2018-02-27 22:24:26 -08:00
59fa37252f meta/bandwidth: group and sort imports
ref: https://github.com/golang/go/wiki/CodeReviewComments#imports
2018-02-27 20:47:42 -08:00
dce91d11d6 meta/bandwidth: remove boilerplate comments 2018-02-27 20:47:41 -08:00
b78e535055 plugins/meta/bandwith: traffic shaping plugin
Add chained plugin to add a tbf qdisc to shape ingress/egress traffic
2018-02-27 20:47:41 -08:00
372bb5e826 Merge pull request #119 from jzwlqx/master
dhcp: clean duplicated error message
2018-02-21 11:09:15 -06:00
961b53bf2f Merge pull request #122 from rosenhouse/bump-golang-1.10
travis: bump golang versions
2018-02-21 11:08:29 -06:00
c82fb1bbe2 Merge pull request #120 from rosenhouse/get-peer
Adds GetVethPeerIfindex library function
2018-02-21 08:13:15 -08:00
c850d4514d pkg/ip: Tearing down ipmasq should be idempotent 2018-02-21 15:38:16 +01:00
fa2bf4c210 vendor: bump go-iptables 2018-02-21 15:35:43 +01:00
6cb23dc489 travis: bump golang versions
- test against Go 1.10
- stop testing against Go 1.8

since Go language maintainers no longer support 1.8
see: https://golang.org/doc/devel/release.html#policy
2018-02-17 19:25:19 -08:00
8e0f961576 Merge pull request #121 from rosenhouse/fix-go-vet-err
bridge test: fix go vet error
2018-02-17 19:23:28 -08:00
69cc860821 bridge test: fix go vet error 2018-02-17 18:51:41 -08:00
0536605966 pkg/ip: GetVethPeerIfindex: add basic test coverage 2018-02-17 16:10:19 -08:00
b16633bbe9 pkg/ip: add GetVethPeerIfindex() 2018-02-17 15:42:10 -08:00
8da8088d43 vendor: add safchain/ethtool 2018-02-17 15:42:05 -08:00
dd8ff8a5cf Merge pull request #113 from nyren/master
Explicitly enable IPv6 sysctl
2018-02-14 16:11:11 +00:00
08ec2995ba Explicitly enable IPv6 sysctl
If CNI is about to configure an IPv6 address, make sure IPv6 is not
disabled through the "disable_ipv6" sysctl setting.

This is a workaround for Docker 17.06 and later which sets
disable_ipv6=1 for all interfaces even if ipv6 is enabled in Docker.
2018-02-13 20:11:06 +01:00
fdc602c0f0 clean duplicated error message 2018-02-12 16:47:53 +08:00
136399f078 Merge pull request #117 from dcbw/dhcp-cleanup
dhcp plugin testcases and bug fixes
2018-02-09 19:23:51 +01:00
596480eadc dhcp: remove leases when they are released
Don't lock around the Stop() operation though, as that may take
a while and block other operations.  That may mean we call Stop()
multiple times, but the Lease object should handle that correctly
itself.
2018-02-08 11:17:38 -06:00
61c3c42107 dhcp: don't try to close the lease stop channel multiple times
Fixes: https://github.com/containernetworking/plugins/issues/105
2018-02-08 11:17:38 -06:00
b522ed6aae plugins/dhcp: add testcase 2018-02-08 11:17:38 -06:00
26ef6e312d vendor: add d2g/dhcp4server 2018-02-07 12:48:08 -06:00
461d433911 Merge pull request #116 from squeed/release-bump-go
release: bump golang version
2018-02-07 14:44:43 +01:00
84a01001be release: bump golang version 2018-02-07 13:16:30 +01:00
e2f063b534 Merge pull request #88 from lyft/ipvlan-master-intf-ipam
ipvlan: support enslaving an interface returned by ipam
2018-01-31 17:17:13 +01:00
9e5836047c Merge pull request #104 from mzahorik/master
Append default route and process route options in compliance with RFC 3442
2018-01-31 15:08:57 +01:00
808d4e20ae Append a default route to the CNI reply if there's a gateway advertised.
Classless static routes (DHCP option 121) are now processed first.
If CSRs exist, static routes (DHCP option 33) and the gateway default
route are ignored as per RFC 3442.
2018-01-26 10:06:26 -05:00
3468364f7e Merge branch 'master' into ipvlan-master-intf-ipam 2018-01-25 15:06:06 -08:00
412b6d3128 Merge pull request #111 from s1061123/add_procfsprefx
Add -hostprefix in DHCP daemon to run the daemon as container
2018-01-25 12:00:51 +01:00
9604565b22 Add -hostprefix in DHCP daemon to run the daemon as container
This diff adds -hostprefix option in dhcp daemon. This option
could be used to run dhcp daemon as container because container
cannot touch host's netns directly. The diff changes dhcp daemon
to touch procfs mounted to another path, like '/hostfs/proc'.
2018-01-25 02:00:43 +09:00
2a0736c748 Merge pull request #97 from oilbeater/fix/link-leak
delete link and ip if err when cmdAdd to avoid resource leak.
2018-01-24 16:38:44 +00:00
2eba56ad52 Merge pull request #100 from squeed/range-arg
ipam/host-local: Accept ip ranges as a runtime argument
2018-01-24 15:45:39 +01:00
d228f980e1 Merge pull request #103 from alice02/fix/skip_settleaddress
pkg/ipam: Skip ip.SettleAddresses if only IPv4 is used
2018-01-22 12:07:41 +01:00
6aa21c431e Merge pull request #107 from zhsj/fix-ipforward
pkg/ip: don't write to /proc/sys if ipforward enabled
2018-01-20 21:38:23 -06:00
c42470bc79 Merge pull request #110 from dcbw/host-device-honor-ifname
host-device: respect CNI_IFNAME/args.IfName
2018-01-19 19:05:29 +01:00
ffc591e242 host-device: respect CNI_IFNAME/args.IfName
On ADD save the host device's name into its IFLA_ALIAS property and
rename the device to the requested CNI_IFNAME inside the container
to conform to the CNI specification.  On DEL rename the device to
the original name and move it back into the host namespace.
2018-01-17 14:30:22 -06:00
59f9976017 pkg/ip: don't write to /proc/sys if ipforward enabled
This enables setup in a container env like systemd nspawn
where /proc/sys is mouted as read only.

Signed-off-by: Shengjing Zhu <i@zhsj.me>
2018-01-18 01:52:49 +08:00
c26961a990 Merge pull request #108 from jingax10/ipvlan_plugin_branch
Update IPVLAN modes by adding l3s in README.
2018-01-17 08:04:57 -08:00
8ebea58550 Update IPVLAN modes by adding l3s in README. 2018-01-12 12:06:48 -08:00
97664d8a6a pkg/ipam: Skip ip.SettleAddresses if only IPv4 is used
This change improves the performance of the ipam.ConfigureIface.
Some plugins are slow because of the ip.SettleAddress in ipam.ConfigureIface.
It seems to be only needed for IPv6, so should be skipped if only IPv4 is used.
2017-12-26 16:48:57 +09:00
5c7e7c0913 ipvlan: support chaining for master interface and IP configuration
For IP allocation schemes that cannot be interface agnostic, the
ipvlan plugin can be chained with an earlier plugin that handles this
logic. If "master" is omitted from the ipvlan configuration, then the
previous Result must contain a single interface name for the ipvlan
plugin to enslave. If "ipam" is omitted, then the previous Result is
used to configure the ipvlan interface.
2017-12-23 10:59:48 -08:00
b03d23a4fa ipam/host-local: Accept ip ranges as a runtime argument
This allows for the runtime to dynamically request IP ranges.

Fixes: #95
2017-12-11 13:51:01 +01:00
03e316b07b Merge pull request #99 from rosenhouse/add-appveyor-badge
README: add Windows CI badge
2017-12-06 18:21:47 +01:00
ecdd827d3a README: add badge for appveyor (Windows CI) 2017-12-03 14:22:36 -08:00
1f02326d56 delete link and ip if err when cmdAdd to avoid resource leak. 2017-11-27 15:26:07 +08:00
92c634042c Merge pull request #93 from squeed/host-device
plugins/main/host-device: generate result, fix DEL, other cleanups
2017-11-22 10:09:32 -06:00
2c05055101 Merge pull request #81 from squeed/portmap-hairpin
portmap: support hairpin, improve performance
2017-11-15 17:34:35 +01:00
5e830efb20 plugins/main/host-local: generate result, fix DEL, other cleanups
This plugin needed some cleaning up: it didn't generate output, and
didn't test DEL. Add those things, plus a README.
2017-11-15 16:00:32 +01:00
73fdc87395 Merge pull request #92 from squeed/host-device
plugins/host_device: move to "main" folder
2017-11-14 22:08:16 -08:00
d07d2aaf71 plugins/host_device: move to "main" folder 2017-11-13 18:50:20 +01:00
6c2ef734c2 Merge pull request #77 from rakelkar/rakelkar/host-local-windows
host-local: Update host-local IPAM to support Windows
2017-11-13 07:37:10 -08:00
47668f6d64 host-local: Update host-local IPAM to support Windows 2017-11-11 15:17:45 -08:00
fbced0cccb Merge pull request #84 from rosenhouse/add-windows-ci
Enable Windows CI (Appveyor)
2017-11-10 18:59:16 -08:00
99f6be0319 Enable Windows CI (Appveyor)
- start list of linux_only plugins; ignore them when testing on Windows
- Isolate linux-only code by filename suffix
- Remove stub (NotImplemented) functions
- other misc. fixes for Windows compatibility
2017-11-10 08:09:29 -08:00
b09e0d28a7 Merge pull request #89 from dcbw/del-link-by-name-addr-fix
pkg/ip: don't return error from DelLinkByNameAddr() if no addresses exist
2017-11-10 17:06:29 +01:00
5576f3120e portmap: support hairpin, improve performance
This change improves the performance of the portmap plugin and fixes
hairpin, when a container is mapped back to itself.

Performance is improved by using a multiport test to reduce rule
traversal, and by using a masquerade mark.

Hairpin is fixed by enabling masquerading for hairpin traffic.
2017-11-10 16:56:52 +01:00
449700f7ea pkg/ip: don't return error from DelLinkByNameAddr() if no addresses exist
For some reason no addresses on the interface returned an error, despite
having a testcase that explicitly tested for success.
2017-11-07 16:07:04 -06:00
4779f1d2bf ipvlan: support enslaving an interface returned by ipam
For IP allocation schemes that cannot be interface agnostic, master can be set
to "ipam". In this configuration, the IPAM plugin is required to return a single
interface name for the ipvlan plugin to enslave.
2017-11-01 10:14:04 -07:00
7f98c94613 Merge pull request #65 from rosenhouse/golang-to-1.9
Golang versions: add 1.9, drop 1.7
2017-10-18 15:36:34 -07:00
596b44301b Vagrantfile: update to golang 1.9.1 2017-10-11 21:19:16 +02:00
0063a1b9d0 Merge pull request #78 from rmohr/dhcp
Don't let DHCP IPAM plugin fail on missing lease
2017-10-11 14:50:19 +01:00
cc71426592 Don't let DHCP delete fail on missing lease
There are at least two reasons why a lease is not present:

 * The dhcp ipam daemon was restarted
 * On add the IPAM plugin failed

Don't fail the IPAM invocation when the lease is not present, to allow
proper device cleanup on CNI delete invocations.
2017-10-11 14:29:16 +02:00
e256564546 Merge pull request #74 from rosenhouse/host-device-fixes
Host-device fixes
2017-09-13 11:41:14 +02:00
c238c93b5e host-device plugin: result is valid JSON
test:
- feed valid config JSON to plugin
- execute plugin inside the namespace with the test device
2017-09-12 21:01:58 -07:00
25ca6ccb52 host-device: do not swallow netlink errors 2017-09-12 20:53:35 -07:00
5e46a66c89 Fix go get github.com/containernetworking/plugins.
Signed-off-by: Lantao Liu <lantaol@google.com>
2017-09-12 05:08:38 +00:00
6be2e8a0e2 Merge pull request #3 from trusch/master
added host-device plugin which adds a specified link to container
2017-09-11 18:51:21 +02:00
b24225fc17 Merge pull request #72 from rosenhouse/move-echosvr
testing: move echosvr into testutils
2017-09-07 14:22:03 +02:00
d8f2fd7a3c testing: move echosvr into testutils 2017-09-06 19:10:48 -07:00
1396ab0bab Merge pull request #63 from squeed/v6-fixes
Fix ipmasq teardown on v6-only interfaces
2017-09-06 15:36:24 -05:00
92babd4a3d Merge pull request #71 from rosenhouse/fix-portmap-integ-test
portmap integration test: echo server runs in separate process
2017-09-06 13:13:03 -05:00
7a62515407 pkg/ip: Fix ipmasq teardown on v6-only interfaces 2017-09-06 20:02:41 +02:00
008024125a portmap integration test: echo server runs in separate process
this way we're not mixing goroutines and namespaces
2017-09-05 23:36:12 -07:00
556e509097 Merge pull request #64 from rosenhouse/travis-ginkgo
travis: run with ginkgo -p instead of go test
2017-08-31 14:28:55 +02:00
dda9c2b1b0 travis: run with ginkgo -p instead of go test
may help reduce test-pollution due to namespace-affinity

see http://onsi.github.io/ginkgo/#parallel-specs
2017-08-30 20:22:12 -07:00
0e3df2961c Merge pull request #67 from rosenhouse/test-all-packages
Test all packages
2017-08-30 19:32:48 -07:00
e1ea7f5ecb Test all non-vendored packages 2017-08-30 14:47:10 -07:00
8fe8460c72 Merge pull request #66 from rosenhouse/filelock-vet
host-local FileLock is used by value instead of by reference
2017-08-30 08:21:28 -07:00
92e62b9f4d test.sh: cover host-local disk backend for go test and go vet 2017-08-30 06:52:29 -07:00
2f957864ea host-local disk backend store uses FileLock by reference
- this change fixes go vet warnings for the package
2017-08-30 06:52:29 -07:00
b49379d284 Merge pull request #62 from squeed/bridge-fixes
bridge: various fixes
2017-08-29 21:00:21 -07:00
9769434a13 Golang versions: add 1.9, drop 1.7 2017-08-29 20:15:50 -07:00
a124fb36e6 bridge: various fixes
* Don't set the MAC, send gratuitous arp instead
* Set the bridge's MAC to itself
* Only disable DAD when necessary
2017-08-28 18:12:49 +02:00
9fb22524a1 Merge pull request #54 from squeed/clean-build
Release: clean the builddir when building
2017-08-21 15:39:28 +02:00
2d7d680874 Release: clean the builddir when building 2017-08-14 14:42:50 +02:00
7480240de9 Merge pull request #50 from squeed/host-local-multi
ipam/host-local: support sets of disjoint ranges
2017-08-11 11:59:55 -05:00
27d027a6d3 ipam/host-local: support sets of disjoint ranges
In real-world address allocations, disjoint address ranges are common.
Therefore, the host-local allocator should support them.

This change still allows for multiple IPs in a single configuration, but
also allows for a "set of subnets."

Fixes: #45
2017-08-09 19:02:08 +02:00
5544d9ced0 Merge pull request #47 from dcbw/tuning-prev-result
tuning: pass prevResult through
2017-07-31 15:19:46 -05:00
998a0f6c6a tuning: pass prevResult through 2017-07-26 14:39:01 -05:00
20bc33abc5 Merge pull request #44 from verizonlabs/build-on-osx
If we're on OS X then have Go cross compile for us
2017-07-21 11:48:06 -07:00
bde6140771 If we're on OS X then have Go cross compile for us 2017-07-20 12:03:30 -04:00
a714098daf Merge pull request #35 from dnardo/bridge-plugin
bridge: add support for promiscuous mode
2017-07-12 08:30:00 -07:00
409be3d304 Merge pull request #40 from squeed/portmap-timeout
plugins/portmap: fix test flake
2017-07-12 08:18:09 -07:00
3b41be3784 Merge pull request #38 from tomdee/release-process
Add release process
2017-07-12 08:11:39 -07:00
b7b0e6c1d4 Add release process
Copied from containernetworking/cni

The only difference is that the release archives also include the the
word "plugin"
2017-07-11 13:57:49 -07:00
3745ee2d3f plugins/portmap: fix test flake
The source address selection was random, and sometimes we picked a
source address that the container didn't have a route to. Adding a
default route fixes that!
2017-07-06 16:12:24 +02:00
56d8984bbc Merge pull request #37 from containernetworking/travis-badge
README.md: Add travis badge
2017-07-06 15:19:15 +02:00
e7328869fa Merge pull request #39 from dcbw/ptp-fix-test
ptp: fix testcase for Interface int-pointer changes
2017-07-05 16:39:48 -05:00
3c9f1b16d6 Merge pull request #36 from tomdee/vendor-libcni-v0.6.0-rc1
Update vendor to v0.6.0-rc1
2017-07-05 16:36:50 -05:00
0b9dbef185 ptp: fix testcase for Interface int-pointer changes 2017-07-05 16:26:19 -05:00
55cdf255db README.md: Add travis badge 2017-07-05 11:49:07 -07:00
ba6a709062 Update vendor to v0.6.0-rc1 2017-07-05 11:46:00 -07:00
9d08b04a3a bridge: add support for promiscuous mode 2017-07-05 09:51:10 -07:00
a690f775ae Merge pull request #25 from squeed/ptp-v6
ptp: add ipv6 support
2017-07-05 17:30:23 +02:00
15f6798bf9 Merge pull request #34 from dnardo/netlink-update
vendor: Update vishvanana/netlink dependency.
2017-07-05 08:29:53 -07:00
74054da7c5 Merge pull request #28 from dcbw/update-for-interface-change
pkg,plugins: update for Result struct Interface index changes
2017-07-05 10:29:05 -05:00
7716c027cf vendor: Update vishvanana/netlink dependency.
This updates the netlink dependency to include recent updates, including a fix when setting prosmic mode on a bridge and additions for creating qdisc/classes/filters. This is necessary for some upcoming additions to CNI
2017-06-30 15:18:01 -07:00
3daee3214d ptp: add ipv6 support
* Wait for addresses to leave tentative state before setting routes
* Enable forwarding correctly
* Set up masquerading according to the active protocol
2017-06-30 12:06:57 +02:00
f8134be6ef Merge pull request #33 from squeed/bump-iptables
vendor: bump go-iptables to 0.2.0
2017-06-30 11:59:44 +02:00
0da6780449 pkg,plugins: update for Result struct Interface index changes
It's a pointer now, so we need to use the helper function to set
the field and also test for nil before accessing it.
2017-06-29 13:40:36 -05:00
8185e7c59c vendor: bump go-iptables to 0.2.0 2017-06-29 16:12:17 +02:00
ca3f28fa9e host-device: cleanup + completed tests; 2017-06-29 11:44:59 +02:00
121362601b vendor: bump cni to 0.5.2-git49d814c 2017-06-28 12:06:37 -05:00
5a7a8b3069 Merge pull request #24 from squeed/flannel-return
flannel: set the cniVersion on the delegated config
2017-06-28 08:45:02 -05:00
f01230d75f Merge pull request #10 from leblancd/v6_bridge_plugin
bridge: Add support for IPv6 to bridge plugin
2017-06-28 08:34:53 -05:00
ffdc748861 bridge: Add support for IPv6 to bridge plugin
This change adds support for IPv6 container/pod addresses to the CNI
bridge plugin, both for dual-stack (IPv4 + IPv6) and for IPv6-only
network configurations.

The proposed changes support multiple IPv6 addresses on a container
interface. If isGW is configured, the bridge will also be configured with
gateway addresses for each IPv6 subnet.

Please note that both the dual-stack functionality and support for multiple
IPv6 container/gateway addresses depends upon containernetworking/cni
PR 451 "ipam/host-local: support multiple IP ranges".
This change could potentially be committed independently from this host-local
plugin change, however the dual-stack and multiple IPv6 address
functionality that is enabled by this change can't be exercised/tested
until the host-local plugin change is committed.

There are some IPv6 unit test cases that are currently commented out
in the proposed changes because these test cases will fail without the
prior commits of the multiple IP range host-local change.

This pull request includes a temporary workaround for Kubernetes
Issue #32291 (Container IPv6 address is marked as duplicate, or dadfailed).
The problem is that kubelet enables hairpin mode on bridge veth
interfaces. Hairpin mode causes the container/pod to see echos of its
IPv6 neighbor solicitation packets, so that it declares duplicate address
detection (DAD) failure. The long-term fix is to use enhanced-DAD
when that feature is readily available in kernels. The short-term fix is
to disable IPv6 DAD in the container. Unfortunately, this has to be done
unconditionally (i.e. without a check for whether hairpin mode is enabled)
because hairpin mode is turned on by kubelet after the CNI bridge plugin
has completed cmdAdd processing. Disabling DAD should be okay if
IPv6 addresses are guaranteed to be unique (which is the case for
host-local IPAM plugin).
2017-06-21 12:47:38 -04:00
8c5e754617 flannel: set the cniVersion on the delegated config
Fixes: #23
2017-06-16 19:04:07 +02:00
5bbff37294 Merge pull request #16 from squeed/gratuitous-arp
ptp, macvlan: don't change mac address; send gratuitous arp
2017-06-16 18:34:37 +02:00
485f151536 Merge pull request #22 from squeed/portmap-plugin
portmap: Don't accept a port number of 0
2017-06-16 18:33:40 +02:00
494053dd98 Merge pull request #11 from dcbw/dhcp-pidfile
dhcp: add --pidfile option to DHCP client daemon
2017-06-15 22:48:42 -07:00
83ec9bb73f portmap: Don't accept a port number of 0
This also adds more testing around configuration parsing.
2017-06-15 14:00:04 +02:00
e2558a03bb Merge pull request #12 from squeed/host-local-multi
ipam/host-local: support multiple IP ranges
2017-06-14 21:55:30 -05:00
a38dd5f7a0 dhcp: add --pidfile option to DHCP client daemon 2017-06-14 21:41:43 -05:00
45650feb32 Merge pull request #14 from rosenhouse/more-ns-warning-redux
pkg/ns readme: expand on danger of ns switching in long-lived programs
2017-06-14 16:18:26 +01:00
2e9e87732f ipam/host-local: support multiple IP ranges
This change allows the host-local allocator to allocate multiple IPs.
This is intended to enable dual-stack, but is not limited to only two
subnets or separate address families.
2017-06-12 21:14:44 +02:00
7cda9af13f Merge pull request #17 from tklauser/x-sys-unix-setns
pkg/ns: use unix.Setns() instead of open coding it
2017-06-08 08:39:41 -07:00
9c7aeff02c pkg/ns: use unix.Setns() instead of open coding it
Use unix.Setns() instead of manually re-implementing it using
unix.Syscall().
2017-06-08 14:48:58 +02:00
98cafb157c Merge pull request #15 from rosenhouse/contribute-vagrantfile
Add CONTRIBUTING and Vagrantfile
2017-06-07 07:26:01 -07:00
e76165b44f ptp, macvlan: don't change mac address; send gratuitous arp 2017-06-07 16:08:41 +02:00
94e6489466 vendor: add j-keck/arping 2017-06-07 15:40:26 +02:00
9a96aa2981 add Vagrantfile to support cross-platform development
Update CONTRIBUTING to reflect plugin split
2017-06-06 20:13:31 -07:00
a860d4d244 contributing doc: copy in from cni repo 2017-06-06 19:20:19 -07:00
6f45630cc1 pkg/ns readme: expand on danger of ns switching in long-lived programs
- redux of https://github.com/containernetworking/cni/pull/331 since the
plugin/lib split
- added link to new blog post about this from Weave folks (thanks!)
2017-06-06 19:05:00 -07:00
e8bea554c5 Merge pull request #13 from squeed/build-fixes
build: some minor post-repo-split fixes; add fastbuild
2017-06-02 10:23:03 +02:00
43a24515ee build: some minor post-repo-split fixes; add fastbuild 2017-06-01 18:51:46 +02:00
0997c532a1 Merge pull request #1 from squeed/portmap-plugin
iptables-based portmapping plugin
2017-06-01 13:05:29 +01:00
a7aaf0e377 plugins/meta/portmap: add an iptables-based host port mapping plugin 2017-06-01 10:06:28 +02:00
22cda76afb vendor: add mattn/go-shellwords 2017-05-31 19:21:03 +02:00
b47d5b7c0a vendor: add libcni 2017-05-31 18:32:38 +02:00
539174f8c8 vendor: update coreos/go-iptables 2017-05-31 18:32:38 +02:00
8816992fcb Merge pull request #9 from squeed/move-pkg
Move libraries from cni only used by plugins.
2017-05-31 10:11:59 -05:00
4e2f467e21 vendor: remove moved cni/pkg/... libraries 2017-05-23 16:12:53 +02:00
3c436520be move most of cni/pkg to plugins/pkg: delete code staying in cni/pkg
This moves the following packages to this repository:
* ip
* ipam
* ns
* testutils
* utils
2017-05-23 16:12:49 +02:00
8b43e90949 Add 'pkg/' from commit 'a11be4d7596203874b742b6597caf255204c56c3'
git-subtree-dir: pkg
git-subtree-mainline: dcf7368eea
git-subtree-split: a11be4d759
2017-05-19 19:43:02 +02:00
dcf7368eea Merge pull request #6 from squeed/merge-docs
Documentation folder merge
2017-05-19 16:16:01 +02:00
0c1c1b7d8c Merge pull request #8 from squeed/bump-cni
vendor: bump cni to v0.5.2
2017-05-19 15:54:54 +02:00
f5e4c117d1 vendor: bump cni to v0.5.2 2017-05-19 14:38:26 +02:00
c9e3fc4890 Add references to all plugins to the README 2017-05-16 16:46:24 +02:00
9f52fc0374 Move documentation folder to individual READMEs 2017-05-16 16:46:15 +02:00
3ac78df0b6 Add 'Documentation/' from commit '5bb59a12b80dc99552bebf0394a067807f72f947'
git-subtree-dir: Documentation
git-subtree-mainline: db8f94a509
git-subtree-split: 5bb59a12b8
2017-05-15 17:13:31 +02:00
db8f94a509 Rename build & test, take two 2017-05-15 16:50:24 +02:00
1430c43243 Rename build script to avoid conflict with bazel 2017-05-15 16:45:58 +02:00
f130db5b61 Merge pull request #5 from dcbw/add-cni-plugins
plugins: add plugins from containernetworking/cni
2017-05-15 12:27:40 +02:00
d2792f264e plugins: add plugins from containernetworking/cni
Plugins prepared from the containernetworking/cni repo as follows:

1) git reset --hard 1a9288c3c0
2) git remove everything not in plugins/
3) git remove plugins/test
4) git merge into containernetworking/plugins repo
5) adjust import paths for containernetworking/cni -> containernetworking/plugins
2017-05-14 23:12:45 -05:00
4e0349a7e7 Remove everything except for plugins in preparation for import to plugins repo 2017-05-14 23:04:53 -05:00
4af68521cd plugins: move vlan to plugins/main/ 2017-05-09 22:47:26 -05:00
7cab2fd317 vendor: add github.com/d2g 2017-05-09 22:45:44 -05:00
be5f1f723e vendor: add coreos/go-systemd 2017-05-09 22:45:44 -05:00
8c27b64f13 vendor: add gomega/gbytes and gomega/gexec 2017-05-09 22:45:44 -05:00
af9127b7ea vendor: bump CNI to 1a9288c3c0 2017-05-09 22:45:44 -05:00
1a9288c3c0 Merge pull request #450 from gunjan5/spec-version-mismatch
cniVersion and config structure mismatch
2017-05-08 11:12:25 +02:00
e03c76b852 Merge pull request #445 from feiskyer/cniversion
Add cniVersion to Result
2017-05-05 17:47:26 -05:00
efe4f37afa cniVersion and config structure mismatch 2017-05-05 14:50:58 -07:00
c44a76ddd3 Merge pull request #4 from containernetworking/plugins-travis
travis integration
2017-05-05 17:10:19 +02:00
b686cad062 Merge pull request #447 from gunjan5/invalid-spec-json
Fix invalid json
2017-05-04 16:06:04 -07:00
5e3be5a44f fix invalid json 2017-05-04 15:59:11 -07:00
1e3715157c travis integration
- go build instead of go install to support cross-compile to bin dir
- shellcheck fixes for build script
2017-05-03 12:22:14 -07:00
a11be4d759 Fix testings accross the project
Signed-off-by: Pengfei Ni <feiskyer@gmail.com>
2017-05-03 23:21:44 +08:00
f197c01b62 Fix testings accross the project
Signed-off-by: Pengfei Ni <feiskyer@gmail.com>
2017-05-03 23:21:44 +08:00
3cd4e26aeb Add cniVersion to Result
Signed-off-by: Pengfei Ni <feiskyer@gmail.com>
2017-05-03 21:38:42 +08:00
27a5b994c2 Add cniVersion to Result
Signed-off-by: Pengfei Ni <feiskyer@gmail.com>
2017-05-03 21:38:42 +08:00
f2faf549b4 [host-device] integrated getLink() function which maps either devicename, hw-addr or kernelpath to a link object; 2017-05-02 13:21:50 +02:00
231f6c4c53 Merge pull request #433 from dunjut/master
scripts: cleanup() should rm net container in docker-run.sh
2017-04-28 20:58:17 -05:00
a6c96acda4 Merge pull request #440 from feiskyer/cap
cnitool: support capablity args
2017-04-28 20:55:31 -05:00
6099d8c84c added host-device plugin which adds a specified link to the container network namespace; 2017-04-28 09:34:22 +02:00
c11286b835 Merge pull request #2 from dcbw/vlan
plugins: add a simple VLAN plugin
2017-04-26 12:54:05 -05:00
010c8a9503 Merge pull request #439 from dnardo/netlink-update
vendor: Update vishvanana/netlink dependency.
2017-04-25 10:24:05 -05:00
73a2d131ee cnitool: support capablity args
Signed-off-by: Pengfei Ni <feiskyer@gmail.com>
2017-04-25 17:35:03 +08:00
21d96393ea Merge pull request #391 from tangle329/master
Validate rangeStart and rangeEnd specified in conf
2017-04-24 22:00:07 -05:00
8eddd61fed Merge pull request #435 from micahhausler/patch-1
Fixed table formatting
2017-04-24 11:21:33 -07:00
0de29de33e Update netlink dependency. 2017-04-24 10:31:24 -07:00
87fbabeeef Fixed table formatting 2017-04-21 15:22:56 -04:00
du
6ee2048ec2 scripts: cleanup() should rm net container in docker-run.sh 2017-04-21 18:27:46 +08:00
279cae5ec5 Merge pull request #432 from dunjut/master
README: build the plugins using ./build.sh, not ./build
2017-04-21 11:12:49 +01:00
du
43c11dda15 README: build the plugins using ./build.sh, not ./build 2017-04-21 15:29:57 +08:00
076080eedf vlan: add VLAN plugin 2017-04-19 13:41:12 -05:00
6b3bf7ea7b vendor: add CNI invoke, ip, ipam, and hwaddr packages 2017-04-18 23:49:26 -05:00
7309649a66 test: CmdAddWithResult sets CNI_PATH from PATH
To actually use CNI plugins in the given CNI_PATH, we need to add
CNI_PATH to PATH because CmdAddWithResult() does this:

os.Setenv("CNI_PATH", os.Getenv("PATH"))
2017-04-18 23:49:26 -05:00
9ef6f5f723 test: make tests actually work when packages have vendored imports
Go's "..." syntax (eg, ./plugins/...) doesn't traverse symlinks, so
go test wasn't finding the vendor/ directory for imports.  To get around
that we have to specify each testable package specifically rather
than use "...".
2017-04-18 23:40:46 -05:00
4a2f3ddd5d Merge pull request #430 from kris-nova/remove-proposal
Wondering if this is still a proposal?
2017-04-16 11:13:24 -07:00
392e1bbd10 Changing Proposal->Specification in Spec 2017-04-16 11:01:23 -06:00
5bb59a12b8 Merge pull request #419 from asridharan/cnitool
Added documentation for `cnitool`
2017-04-07 10:20:07 -07:00
137b4975ec Merge pull request #419 from asridharan/cnitool
Added documentation for `cnitool`
2017-04-07 10:20:07 -07:00
7d3fe6923b Documentation: Added documentation for cnitool.
Added documentation to simplify the usage of `cnitool` and the
settings expected to be used with `cnitool`.
2017-04-05 21:40:24 +00:00
382686ed63 Documentation: Added documentation for cnitool.
Added documentation to simplify the usage of `cnitool` and the
settings expected to be used with `cnitool`.
2017-04-05 21:40:24 +00:00
d427963784 libcni: Fixed tests that were checking error strings.
Checking error strings makes these tests flaky, especially if the
error string is changed in libcni. Have gone ahead an introduced a new
error type `NoConfigsFoundError` and the Match is against the error
type making it more deterministic.
2017-04-05 21:40:24 +00:00
5b0d6a0c8f libcni: Improved error messages.
Improved the error reporting to be more descriptive when configuration
files are not filed while using the cni-tool.
2017-04-05 21:40:16 +00:00
361ec957fb Merge pull request #413 from dcbw/spec-fix-ips
spec/plugins: fix 'ip'->'ips' in the spec, bump to 0.3.1
2017-04-05 14:39:44 -07:00
29ba54c045 spec/plugins: fix 'ip'->'ips' in the spec, bump to 0.3.1 2017-04-05 12:46:24 -05:00
8447ebc83a spec/plugins: fix 'ip'->'ips' in the spec, bump to 0.3.1 2017-04-05 12:46:24 -05:00
9445711fa5 spec/plugins: fix 'ip'->'ips' in the spec, bump to 0.3.1 2017-04-05 12:46:24 -05:00
d4bbce1865 Merge pull request #423 from rosenhouse/update-travis
travis: shift forward to Go 1.8 and 1.7
2017-04-05 09:26:49 -07:00
ff92701693 Merge pull request #421 from kshafiee/patch-1
Added entry for CNI-Genie
2017-04-05 09:24:21 -07:00
a39d6845fa travis: shift forward to Go 1.8 and 1.7 2017-04-05 09:11:35 -07:00
d66edc3180 Merge pull request #400 from lstoll/lstoll-rename-build-script
Rename build script to avoid conflict with bazel
2017-04-05 11:03:23 -05:00
aaba1b0224 Added entry for CNI-Genie
CNI-Genie enables orchestrators (kubernetes, mesos) for seamless connectivity to choice of CNI plugins (calico, canal, romana, weave) configured on a Node
2017-04-03 12:00:02 -07:00
470e86eb8b Update community sync detail 2017-03-31 14:38:48 -07:00
73c6616946 Merge pull request #414 from kad/s390x
Enable s390x build
2017-03-27 10:10:06 -07:00
61f3b4b315 Enable s390x build 2017-03-23 00:51:29 +02:00
0799f5732f Merge pull request #412 from tomdee/safe-del
plugins/*: Don't error if the device doesn't exist
2017-03-22 09:47:29 -07:00
58b78e72fd plugins/*: Don't error if the device doesn't exist
I wasn't able to test or update the dhcp plugin but from a code read it
should be fine. All the other plugins are tested and fixed
2017-03-22 08:52:29 -07:00
13824487c6 plugins/*: Don't error if the device doesn't exist
I wasn't able to test or update the dhcp plugin but from a code read it
should be fine. All the other plugins are tested and fixed
2017-03-22 08:52:29 -07:00
699380d687 Merge pull request #407 from aaronlevy/del-err
plugins/meta/flannel: If net config is missing do not return err on DEL
2017-03-21 10:43:40 -07:00
74d4cbed76 plugins/meta/flannel: If net config is missing do not return err on DEL 2017-03-17 13:37:33 -07:00
b87126377a Merge pull request #396 from cf-container-networking/vendored-types-leakage
pkg/ip: do not leak types from vendored netlink package
2017-03-16 19:20:40 -07:00
c235b448d4 Merge pull request #401 from chrismarino/patch-1
Added Romana to list of CNI providers...
2017-03-15 12:38:04 -07:00
c1c7826922 Added Romana to list of CNI providers...
Added Romana to list of CNI providers.
2017-03-15 12:10:21 -07:00
1b9caefba5 Rename build script to avoid conflict with bazel
It's difficult to include this repository using bazel, because
the file named "build" conflicts with new_go_repository generation
on case-insensitive filesystems (ref
https://github.com/bazelbuild/rules_go/issues/234). This change
renames the file to something that doesn't conflict, and also
renames the test script for consistency.
2017-03-15 15:59:44 +11:00
bb322572eb pkg/ip: improve docstring for SetupVeth 2017-03-13 11:27:12 -07:00
f51dd618c5 pkg/ip: improve docstring for SetupVeth 2017-03-13 11:27:12 -07:00
559ffa1111 Add simple testing infrastructure 2017-03-13 18:05:55 +01:00
168a5a0210 godeps: initial creation 2017-03-13 14:36:20 +01:00
d62b9a0584 sample: create sample plugin 2017-03-13 14:31:58 +01:00
71d96ea91c pkg/ip: SetupVeth returns net.Interface 2017-03-10 09:49:46 -08:00
e4a0583d7a pkg/ip: SetupVeth returns net.Interface 2017-03-10 09:49:46 -08:00
fb4a7f477b Initial commit 2017-03-10 16:46:52 +01:00
1b65890795 Validate rangeStart and rangeEnd specified in conf
Signed-off-by: Tang Le <tangle3@wanda.cn>
2017-03-10 12:51:05 +08:00
b07d768f12 pkg/ip: do not leak types from vendored netlink package
The exported function SetupVeth now returns a package-defined type.

Signed-off-by: Gabe Rosenhouse <grosenhouse@pivotal.io>
2017-03-09 13:55:15 -08:00
58c834c4f3 pkg/ip: do not leak types from vendored netlink package
The exported function SetupVeth now returns a package-defined type.

Signed-off-by: Gabe Rosenhouse <grosenhouse@pivotal.io>
2017-03-09 13:55:15 -08:00
4f36e5994e Merge pull request #393 from containernetworking/community-sync-readme
readme.md: Add link to community sync
2017-03-08 16:46:44 -08:00
5d5b16f0b3 readme.md: Add link to community sync 2017-03-08 15:54:15 -08:00
4ce9b019aa Merge pull request #366 from ehazlett/ipam-host-local-initial-reserved-ip
Do not error if last reserved not found after initial creation
2017-03-06 07:13:06 -08:00
ac50624393 fix unrelated failing tests
Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>
2017-03-02 15:37:43 -05:00
2678be24d0 add test for ensuring initial subnet creation does not contain an error
Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>
2017-03-02 15:16:09 -05:00
474ba16901 do not error if last_reserved_ip is missing for host local ipam
Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>
2017-03-02 13:59:21 -05:00
1f6efc28b5 Merge pull request #387 from tangle329/master
Check n.IPAM before use it in LoadIPAMConfig function
2017-03-02 09:23:02 -08:00
75a5cf1bf0 Merge pull request #373 from dcbw/conflist-runtime-config
spec,libcni: add support for injecting runtimeConfig into plugin stdin data
2017-03-02 11:17:00 -06:00
1003822e53 Check n.IPAM before use it in LoadIPAMConfig function
Signed-off-by: Tang Le <tangle3@wanda.cn>
2017-03-02 10:25:19 +08:00
9b8a0e0c64 spec,libcni: add support for injecting runtimeConfig into plugin stdin data
Add a new CapabilityArgs member to the RuntimeConf struct which runtimes can
use to pass arbitrary capability-based keys to the plugin.  Elements of this
member will be filtered against the plugin's advertised capabilities (from
its config JSON) and then added to a new "runtimeConfig" top-level map added
to the config JSON sent to the plugin on stdin.

Also "runtime_config"->"runtimeConfig" in CONVENTIONS.md to make
capitalization consistent with other CNI config keys like "cniVersion".
2017-03-01 10:49:40 -06:00
71c4a60741 spec,libcni: add support for injecting runtimeConfig into plugin stdin data
Add a new CapabilityArgs member to the RuntimeConf struct which runtimes can
use to pass arbitrary capability-based keys to the plugin.  Elements of this
member will be filtered against the plugin's advertised capabilities (from
its config JSON) and then added to a new "runtimeConfig" top-level map added
to the config JSON sent to the plugin on stdin.

Also "runtime_config"->"runtimeConfig" in CONVENTIONS.md to make
capitalization consistent with other CNI config keys like "cniVersion".
2017-03-01 10:49:40 -06:00
6008de1ffd Merge pull request #382 from rosenhouse/doc-spec-upgrade
Documentation: guidance for upgrading to CNI Spec v0.3.0
2017-03-01 08:45:56 -08:00
c806f3d62b Minor rewording about default config version 2017-02-28 19:44:25 +01:00
09e5feae1f Minor rewording about default config version 2017-02-28 19:44:25 +01:00
0b1b85413e Merge pull request #383 from dcbw/test-fixes
tests: misc test fixes
2017-02-27 16:13:45 -06:00
7d8c23dd59 Merge pull request #383 from dcbw/test-fixes
tests: misc test fixes
2017-02-27 16:13:45 -06:00
d6d012b88f Merge pull request #374 from squeed/config-list-upconvert
libcni: up-convert a Config to a ConfigList when no other configs are found.
2017-02-27 15:10:37 -06:00
1f4a8c2802 Merge pull request #377 from squeed/ptp-sandbox
plugins/main/ptp: set the Sandbox property on the response
2017-02-27 15:06:57 -06:00
fc174a6635 Merge pull request #380 from bboreham/patch-1
Add Bryan Boreham as maintainer
2017-02-27 15:06:26 -06:00
2f1bfcee63 pkg/types: misc current types testcase cleanups 2017-02-27 13:28:07 -06:00
35153e7de8 pkg/types: misc current types testcase cleanups 2017-02-27 13:28:07 -06:00
2a6d12b206 pkg/testutils: return errors after restoring stdout
Ensures Ginkgo is able to print detailed failure messages instead
of them being captured by the pipe.
2017-02-27 13:27:19 -06:00
5a984f0dee pkg/testutils: return errors after restoring stdout
Ensures Ginkgo is able to print detailed failure messages instead
of them being captured by the pipe.
2017-02-27 13:27:19 -06:00
94e02e0768 Merge pull request #375 from aaithal/complieOnNonLinuxPlatforms
pkg/ns: refactored so that builds succeed on non-linux platforms
2017-02-27 11:46:22 -06:00
f15d521b2a docs: table formatting is hard 2017-02-27 07:57:00 -08:00
bbc23ea761 docs: table formatting is hard 2017-02-27 07:57:00 -08:00
5124ac4759 docs: fill-out and correct version conversion table 2017-02-27 07:53:09 -08:00
b6e9bf8372 docs: fill-out and correct version conversion table 2017-02-27 07:53:09 -08:00
2aa863dab7 docs: minor improvements to spec-upgrades 2017-02-27 07:17:37 -08:00
379d47760c docs: minor improvements to spec-upgrades 2017-02-27 07:17:37 -08:00
1061ef1eb9 docs: add small upgrade instructions 2017-02-27 14:57:31 +01:00
119d6f20f5 docs: add small upgrade instructions 2017-02-27 14:57:31 +01:00
bdd70561ad docs: minor improvements to 0.3.0 upgrade guidance 2017-02-27 14:27:56 +01:00
70c153eb41 docs: minor improvements to 0.3.0 upgrade guidance 2017-02-27 14:27:56 +01:00
5b6d955695 docs: Edits to v0.3.0 upgrade guidance 2017-02-26 16:53:52 -08:00
fa53446854 docs: Edits to v0.3.0 upgrade guidance 2017-02-26 16:53:52 -08:00
f658c0b4b3 docs: CNI versioning for 0.3.0 upgrade 2017-02-26 13:26:14 -08:00
328ea56e75 docs: CNI versioning for 0.3.0 upgrade 2017-02-26 13:26:14 -08:00
6e6ad53ea8 libcni: up-convert a Config to a ConfigList when no other configs are found. 2017-02-24 19:44:06 +01:00
1bc8141105 Merge pull request #379 from Intel-Corp/master
README: List multus as 3rd party plugin
2017-02-23 10:40:59 -08:00
460e1c5be3 vendor: Update vishvanana/netlink dependency 2017-02-23 09:53:11 -08:00
7d5619d277 pkg/ip: refactored so that builds succeed on non-linux platforms
moved functions that depend on linux packages into a separate file and added
nop methods with a build tag for non-linux platforms in a new file.
2017-02-23 09:52:13 -08:00
7eeb21a999 pkg/ip: refactored so that builds succeed on non-linux platforms
moved functions that depend on linux packages into a separate file and added
nop methods with a build tag for non-linux platforms in a new file.
2017-02-23 09:52:13 -08:00
46deb7b708 pkg/ns, pkg/types: refactored non linux build fix code to
Make GetCurrentNS platform specific instead of getCurrentThreadNetNSPath
2017-02-23 09:50:48 -08:00
0dc0845c51 pkg/ns, pkg/types: refactored non linux build fix code to
Make GetCurrentNS platform specific instead of getCurrentThreadNetNSPath
2017-02-23 09:50:48 -08:00
aafc728ec0 Merge pull request #371 from rosenhouse/spec-netconf-routes
spec: Remove `routes` from Network Configuration
2017-02-22 20:32:41 -06:00
32246b315c Replace Michael Bridgen with Bryan Boreham 2017-02-22 17:06:04 +00:00
2c33eb4f22 README: List multus as 3rd party plugin
refer #343 issue
2017-02-22 16:13:12 +00:00
9f03c4bc2e Merge pull request #376 from zackhsi/fix-grammar
Fix grammar
2017-02-20 17:51:38 -08:00
7fa16f18d9 plugins/main/ptp: set the Sandbox property on the response 2017-02-21 00:28:19 +01:00
c783679114 Fix grammar 2017-02-20 11:21:49 -08:00
dff808c98e pkg/ns: refactored so that builds succeed on non-linux platforms
moved functions that depend on linux packages (sys/unix) into a separate file
and added nop methods with a build tag for non-linux platforms in a new file.
2017-02-20 10:00:55 -08:00
c3cde44480 pkg/ns: refactored so that builds succeed on non-linux platforms
moved functions that depend on linux packages (sys/unix) into a separate file
and added nop methods with a build tag for non-linux platforms in a new file.
2017-02-20 10:00:55 -08:00
3ba89873ac Merge pull request #372 from rosenhouse/consolidate-host-local-docs
docs: consolidate host-local documentation
2017-02-20 06:33:32 -08:00
98eb0d93a1 docs: consolidate host-local documentation
- remove the README.md in the plugins/ipam/host-local (no other plugin
  has this)
- merge that info into the main Documentation/host-local.md
2017-02-15 15:20:13 -08:00
eb2ff7fe57 docs: consolidate host-local documentation
- remove the README.md in the plugins/ipam/host-local (no other plugin
  has this)
- merge that info into the main Documentation/host-local.md
2017-02-15 15:20:13 -08:00
31dafa2f7a spec: Remove routes from Network Configuration
- this is supported by some IPAM plugins but is not required of all of
  them
2017-02-15 15:02:23 -08:00
6f3b0abcc7 Merge pull request #369 from containernetworking/port-mapping
CONVENTIONS.md: Update details on port-mappings
2017-02-14 11:47:59 -08:00
e3da6e1cfb More markups 2017-02-13 10:14:45 -08:00
95ca9d393a Update with feedback 2017-02-09 14:12:33 -08:00
3fbf94a90a CONVENTIONS.md: Update details on port-mappings
After discussion with the CNI maintainers and representatives from Mesos and Kubernetes we've settled on this approach for passing the dynamic port mapping information from runtimes to plugins.

Including it in the plugin config allows "operators" to signal that they expect port mapping information to be passed to a specific plugin (or plugins). This follows the model that config passed in the plugin config should be acted upon by a plugin.

Runtimes can optionally return an error to users if they find no plugin in the chain that supports port mappings.

I'm sure we'd all like to see this merged soon so I'll try to turn any feedback quickly (and I'm happy to get any and all feedback - spellings, unclear working, wrong JSON terminology etc..)
2017-02-08 12:43:04 -08:00
970de4b9e3 Merge pull request #361 from ofiliz/issue-360
invoke: Enable plugin file names with extensions
2017-02-08 10:15:49 -08:00
f85ac247e6 Merge pull request #361 from ofiliz/issue-360
invoke: Enable plugin file names with extensions
2017-02-08 10:15:49 -08:00
a35f8ae914 invoke: Enable plugin file names with extensions
A CNI network configuration file contains the plugin's executable file name.
Some platforms like Windows require a file name extension for executables.
This causes unnecessary burden on admins as they now have to maintain two
versions of each type of netconfig file, which differ only by the ".exe"
extension. A much simpler design is for invoke package to also look for
well-known extensions on platforms that require it. Existing tests are
improved and new tests are added to cover the new behavior.

Fixes #360
2017-02-04 12:01:47 -08:00
271be6c8df invoke: Enable plugin file names with extensions
A CNI network configuration file contains the plugin's executable file name.
Some platforms like Windows require a file name extension for executables.
This causes unnecessary burden on admins as they now have to maintain two
versions of each type of netconfig file, which differ only by the ".exe"
extension. A much simpler design is for invoke package to also look for
well-known extensions on platforms that require it. Existing tests are
improved and new tests are added to cover the new behavior.

Fixes #360
2017-02-04 12:01:47 -08:00
fc7de21fe2 Merge pull request #364 from pires/build_tag
pkg/utils/sysctl/sysctl_linux.go: couple fixes
2017-02-03 14:18:39 -05:00
2e90a8cc33 pkg/utils/sysctl/sysctl_linux.go: fix typo. 2017-02-03 05:39:04 +00:00
4c0b8f7054 pkg/utils/sysctl/sysctl_linux.go: fix typo. 2017-02-03 05:39:04 +00:00
162b73e44f pkg/utils/sysctl/sysctl_linux.go: fix build tag. 2017-02-03 05:38:53 +00:00
246300ccfe pkg/utils/sysctl/sysctl_linux.go: fix build tag. 2017-02-03 05:38:53 +00:00
e663e98444 Merge pull request #359 from ofiliz/issue-355
libcni, pkg\invoke: Use OS-specific list separator when parsing CNI_PATH
2017-02-02 12:35:07 -08:00
46e17b26ff spec, libcni, pkg/invoke: Use OS-agnostic separator when parsing CNI_PATH
Hardcoding the list separator character as ":" causes CNI to fail when parsing
CNI_PATH on other operating systems. For example, Windows uses ";" as list
separator because ":" can legally appear in paths such as "C:\path\to\file".
This change replaces use of ":" with OS-agnostic APIs or os.PathListSeparator.

Fixes #358
2017-01-28 16:47:58 -08:00
0d9a1d70f8 spec, libcni, pkg/invoke: Use OS-agnostic separator when parsing CNI_PATH
Hardcoding the list separator character as ":" causes CNI to fail when parsing
CNI_PATH on other operating systems. For example, Windows uses ";" as list
separator because ":" can legally appear in paths such as "C:\path\to\file".
This change replaces use of ":" with OS-agnostic APIs or os.PathListSeparator.

Fixes #358
2017-01-28 16:47:58 -08:00
c4271dba67 Merge pull request #145 from dcbw/pass-ifname-back
Pass interface name and MAC back to runtime
2017-01-25 11:20:57 -08:00
06b397912b spec/plugins: return interface details and multiple IP addresses to runtime
Updates the spec and plugins to return an array of interfaces and IP details
to the runtime including:

- interface names and MAC addresses configured by the plugin
- whether the interfaces are sandboxed (container/VM) or host (bridge, veth, etc)
- multiple IP addresses configured by IPAM and which interface they
have been assigned to

Returning interface details is useful for runtimes, as well as allowing
more flexible chaining of CNI plugins themselves.  For example, some
meta plugins may need to know the host-side interface to be able to
apply firewall or traffic shaping rules to the container.
2017-01-25 11:31:18 -06:00
d5acb127b8 spec/plugins: return interface details and multiple IP addresses to runtime
Updates the spec and plugins to return an array of interfaces and IP details
to the runtime including:

- interface names and MAC addresses configured by the plugin
- whether the interfaces are sandboxed (container/VM) or host (bridge, veth, etc)
- multiple IP addresses configured by IPAM and which interface they
have been assigned to

Returning interface details is useful for runtimes, as well as allowing
more flexible chaining of CNI plugins themselves.  For example, some
meta plugins may need to know the host-side interface to be able to
apply firewall or traffic shaping rules to the container.
2017-01-25 11:31:18 -06:00
b89b56dc97 pkg/ipam: add testcases 2017-01-25 11:31:18 -06:00
befad17174 pkg/ipam: add testcases 2017-01-25 11:31:18 -06:00
e6adfe939a macvlan/ipvlan: use common RenameLink method 2017-01-25 11:31:18 -06:00
b0b896f79a plugins/flannel: organize test JSON alphabetically
Otherwise the test fails, since Go's JSON marshaller prints
dict items alphabetically in its String() call.
2017-01-25 11:31:18 -06:00
0d19b79260 types: make Result an interface and move existing Result to separate package 2017-01-25 11:31:18 -06:00
ad2a5ccb61 macvlan/ipvlan: use common RenameLink method 2017-01-25 11:31:18 -06:00
dae1177b53 testutils: pass netConf in for version operations; pass raw result out for tests 2017-01-25 11:31:18 -06:00
befb95977c types: make Result an interface and move existing Result to separate package 2017-01-25 11:31:18 -06:00
cb4cd0e12c testutils: pass netConf in for version operations; pass raw result out for tests 2017-01-25 11:31:18 -06:00
dcadf4f9b5 Merge pull request #355 from rosenhouse/skel-main-errors
skel: adds PluginMainWithError which returns a *types.Error
2017-01-23 07:42:57 -08:00
2515b8c712 skel: adds PluginMainWithError which returns a *types.Error
Enables plugins to do their own error-handling and cleanup logic
2017-01-22 16:57:42 -08:00
af5c2f303a skel: adds PluginMainWithError which returns a *types.Error
Enables plugins to do their own error-handling and cleanup logic
2017-01-22 16:57:42 -08:00
1acc927ab0 Merge pull request #352 from containernetworking/conventions
Documentation: Add conventions doc
2017-01-20 13:50:24 -08:00
04f357344d Update CONVENTIONS.md 2017-01-20 12:49:00 -08:00
e476ab7189 Merge pull request #346 from dcbw/ordered-chaining
api/plugins: implement ordered plugin chaining
2017-01-20 08:35:37 -08:00
13534e505c api,libcni: add network config list-based plugin chaining
Using a new ".configlist" file format that allows specifying
a list of CNI network configurations to run, add new libcni
helper functions to call each plugin in the list, injecting
the overall name, CNI version, and previous plugin's Result
structure into the configuration of the next plugin.
2017-01-19 22:28:21 -06:00
0c2a034f01 api,libcni: add network config list-based plugin chaining
Using a new ".configlist" file format that allows specifying
a list of CNI network configurations to run, add new libcni
helper functions to call each plugin in the list, injecting
the overall name, CNI version, and previous plugin's Result
structure into the configuration of the next plugin.
2017-01-19 22:28:21 -06:00
7e16c86a52 spec: add network configuration list specification 2017-01-19 22:28:20 -06:00
947b79e334 Spec/Conventions: Update to include plugin config 2017-01-19 18:05:28 -08:00
0a1b48f520 noop: allow specifying debug file in config JSON
Chaining sends different config JSON to each plugin, but the same
environment, and if we want to test multiple noop plugin runs in
the same chain we need a way of telling each run to use a different
debug file.
2017-01-19 09:56:42 -06:00
d716d73444 Documentation: Add conventions doc 2017-01-17 15:53:24 -08:00
530dd71e19 Merge pull request #334 from squeed/host-local-dns
ipam/host-local: add ResolvConf argument for DNS configuration
2017-01-12 17:06:04 -08:00
fa58dd8889 Merge pull request #350 from rosenhouse/spec-version
spec: notice of version
2017-01-12 13:42:25 -08:00
20fde00ea6 spec: notice of version 2017-01-11 18:49:10 -08:00
cd16252299 ipam/host-local: add ResolvConf argument for DNS configuration
This adds the option `resolvConf` to the host-local IPAM configuration.
If specified, the plugin will try to parse the file as a resolv.conf(5)
type file and return it in the DNS response.
2017-01-11 18:48:35 +01:00
5cde14cd7b ipam/host-local: add ResolvConf argument for DNS configuration
This adds the option `resolvConf` to the host-local IPAM configuration.
If specified, the plugin will try to parse the file as a resolv.conf(5)
type file and return it in the DNS response.
2017-01-11 18:48:35 +01:00
4406607649 Merge pull request #115 from aanm/implementing-ipam-as-lib
ipam/host-local: Move allocator and config to backend
2017-01-10 13:06:46 -08:00
f60111b093 ipam/host-local: Move allocator and config to backend
Signed-off-by: André Martins <aanm90@gmail.com>
2016-12-21 04:43:21 +00:00
98ff61aac3 Merge pull request #325 from squeed/update-maintainer
MAINTAINERS: hi CaseyC!
2016-12-19 11:02:20 -08:00
6dc01a772b MAINTAINERS: hi CaseyC! 2016-12-19 12:38:29 -05:00
e3bcc3e41b Merge pull request #345 from rosenhouse/travis-go-versions
travis: roll forward the versions of Go that we test
2016-12-16 08:06:17 -08:00
bc1d61e3db travis: roll forward the versions of Go that we test 2016-12-15 18:41:07 -08:00
652bae1deb Merge pull request #314 from rosenhouse/noop-helpful-message
plugins/noop: return a helpful message for test authors
2016-12-15 18:35:43 -08:00
47f6604b5a Merge pull request #344 from dcbw/interface-checks
all: assert internal objects implement interfaces
2016-12-15 18:34:57 -08:00
1c1cf2faf7 Merge branch 'master' into interface-checks 2016-12-15 18:20:34 -08:00
ef00f717a6 Merge pull request #341 from dcbw/host-local-trim
host-local: trim whitespace from container IDs and disk file contents
2016-12-15 18:18:25 -08:00
06fd1369aa all: assert internal objects implement interfaces 2016-12-14 17:09:01 -06:00
5852c60bc4 all: assert internal objects implement interfaces 2016-12-14 17:09:01 -06:00
20ae9e156d Merge pull request #340 from vzcambria/master
Fix README.md host-local IPAM range start/end typo
2016-12-09 18:22:08 -06:00
76028d7f8c host-local: trim whitespace from container IDs and disk file contents
It doesn't seem like container IDs should really have whitespace or
newlines in them.  As a complete edge-case, manipulating the host-local
store's IP reservations with 'echo' puts a newline at the end, which
caused matching to fail in ReleaseByID().  Don't ask...
2016-12-09 18:16:38 -06:00
2ba6d20007 Fix README.md host-local IPAM range start/end typo 2016-12-06 10:40:55 -05:00
accac60053 plugins/noop: return a helpful message for test authors
Signed-off-by: Gabe Rosenhouse <grosenhouse@pivotal.io>
2016-12-02 15:04:01 -08:00
b0808a006a Merge pull request #335 from JulienBalestra/json_ext
libcni conf: support .json file extension
2016-12-02 13:33:36 -06:00
a85593ce0d libcni conf: support .json file extension
Content of the files are json, the configuration load should support .json
2016-11-23 10:48:32 +01:00
de002ff25a Merge pull request #322 from cf-container-networking/flannel-cni-stateDir
Allow flannel CNI plugin stateDir to be configurable
2016-11-18 15:27:35 -06:00
475581873d flannel: rename stateDir to dataDir
Rename StateDir to DataDir for flannel CNI plugin
2016-11-17 15:54:29 -06:00
6eac0ee904 flannel: rename stateDir to dataDir
Rename StateDir to DataDir for flannel CNI plugin
2016-11-17 15:54:29 -06:00
46e3a6b7be Merge pull request #330 from bboreham/ns-warning
Expand description of goroutine+netns problems
2016-11-17 08:00:31 -08:00
44ef109483 Expand description of goroutine+netns problems 2016-11-17 15:45:38 +00:00
c20f895fa8 Expand description of goroutine+netns problems 2016-11-17 15:45:38 +00:00
ba6c5af5dc Merge pull request #327 from dcbw/host-local-tests
host-local: add DataDir IPAM conf option and use it for testcases
2016-11-14 08:17:40 -08:00
15de81eac6 flannel: updated flannel test to use pkg/testutils
reworked the flannel test to use testutils CmdAddWithResult and
CmdDelWithResult
2016-11-13 12:16:32 -06:00
8b827d74b3 Merge pull request #328 from markstgodard/noop-cni-args
noop plugin: support multiple CNI_ARGS
2016-11-10 22:24:58 -08:00
48d3b46eb2 noop plugin: support multiple CNI_ARGS
Updated noop plugin to parse the CNI_ARGS pairs, to allow more than just
the DEBUG arg.
2016-11-10 16:04:42 -06:00
7b2006eed8 Merge pull request #324 from lstoll/lstoll-ipvlan-l3s
ipvlan: Support L3-Symmetric mode
2016-11-10 12:53:59 -06:00
e085226f82 host-local: add DataDir IPAM conf option and use it for testcases
Add an e2e host-local plugin testcase, which requires being able
to pass the datadir into the plugin so we can erase it later.
We're not always guaranteed to have access to the default data
dir location, plus it should probably be configurable anyway.
2016-11-10 12:12:16 -06:00
ffbf708406 flannel: update flannel documentation
add optional 'stateDir' to flannel docs
2016-11-09 09:34:35 -06:00
e6113c6517 flannel: update flannel documentation
add optional 'stateDir' to flannel docs
2016-11-09 09:34:35 -06:00
79643855f7 Merge pull request #307 from squeed/backwards-compatability-tests
testing: test invocation of newer plugins with an older libcni
2016-11-07 14:25:09 -08:00
f0daefa63d flannel: add unit tests for config loading
backfill unit tests to add coverage for loadFlannelNetConf and
loadFlannelSubnetEnv
2016-11-05 23:27:14 -05:00
9201f3f1d9 ipvlan: Support ipvlan l3s mode.
This landed in kernel in 4fbae7d
2016-11-05 13:41:07 -07:00
2ad1d04561 vendor: github.com/vishvananda/netlink@a1f8555
Update to current master, to get IPVLAN L3s def.
2016-11-05 13:41:01 -07:00
fa264e6e36 flannel: add integration test suite
add new test for flannel plugin that delegates to the noop plugin and
validates that 'storeDir' can be configurable
2016-11-05 10:47:07 -05:00
b463642ac0 Allow flannel CNI plugin stateDir to be configurable
- Add optional 'stateDir' to flannel NetConf, if not present default to
/var/lib/cni/flannel

Signed-off-by: Jay Dunkelberger <ldunkelberger@pivotal.io>
2016-11-03 17:10:13 -05:00
61d3f4515a testing: test invocation of newer plugins with an older libcni 2016-10-25 16:33:35 +02:00
8c6f6e0a60 testing: test invocation of newer plugins with an older libcni 2016-10-25 16:33:35 +02:00
0e09ad29df Merge pull request #312 from tomdee/slack
Add slack badge and link
2016-10-24 16:25:48 -07:00
bb8bcf08a8 Add slack badge and link 2016-10-24 16:23:04 -07:00
194f8ce6ea Merge pull request #311 from rkamudhan/patch-2
Update ipvlan.md
2016-10-23 16:23:55 -07:00
17b2a6d5e2 Update ipvlan.md
invalid json format
2016-10-23 23:18:44 +01:00
6e77c7f4f7 Update ipvlan.md
invalid json format
2016-10-23 23:18:44 +01:00
bff05e7572 Merge pull request #308 from rosenhouse/fix-skel-tests-containerid
skel tests: correct name of CNI_CONTAINERID in tests of env vars
2016-10-17 15:02:27 -07:00
cfb2ff3dcd skel tests: correct name of CNI_CONTAINERID in tests of env vars 2016-10-16 17:18:57 -07:00
8be31e1c6f skel tests: correct name of CNI_CONTAINERID in tests of env vars 2016-10-16 17:18:57 -07:00
3450f08611 Merge pull request #305 from yuewko/add_infoblox_link
Add link to Infoblox IPAM plugin
2016-10-16 17:08:51 -07:00
c24fce2ae8 Add link to Infoblox IPAM plugin
Added link to Infoblox IPAM plugin in README.
2016-10-11 11:07:30 -07:00
d872391998 Merge pull request #304 from danp/patch-1
docs/ptp: fix markdown formatting
2016-10-09 22:39:31 -07:00
0ee270c769 docs/ptp: fix markdown formatting 2016-10-09 21:22:27 -03:00
85ab5c0f21 docs/ptp: fix markdown formatting 2016-10-09 21:22:27 -03:00
af88b7c6cf Merge pull request #301 from rosenhouse/backwards-compatibility-tests
A test verifies that libcni is backwards compatible with legacy plugins
2016-10-06 10:10:02 -05:00
165ca6f719 travis: git clone at great depth
ensures that git commits referenced by backwards compatibility tests are
always available in CI.
2016-10-05 20:48:53 -07:00
0087f302ea Merge pull request #302 from rosenhouse/readme-3rd-party-plugins
3rd party plugins belong in their own repos
2016-10-03 08:39:23 -07:00
b2ef4aa1a7 docs: on contributing plugins to this repo 2016-10-02 23:44:59 -07:00
b07b226c97 testing: adds basic test of backwards compatibility 2016-10-02 22:18:53 -07:00
51b51a0182 testing: adds basic test of backwards compatibility 2016-10-02 22:18:53 -07:00
a29fc24f11 Merge pull request #296 from rosenhouse/plugins-require-versioned-config
Plugins validate cniVersion of NetConf
2016-09-22 17:11:34 -05:00
b5e0a4ef2e Merge pull request #298 from freehan/fix-host-local-30
host-local: fix allocation of last address in a /30
2016-09-22 09:13:32 -05:00
52e4358cbd host-local: fix allocation of last address in a /30 2016-09-21 13:10:05 -07:00
e70e6034d2 Merge pull request #299 from tgraf/cilium-link
List cilium as 3rd party plugin
2016-09-19 14:31:54 -07:00
f721eaf6ed skel: use named constant for Incompatible CNI Version error code 2016-09-19 13:25:33 -07:00
5b696f3307 skel: use named constant for Incompatible CNI Version error code 2016-09-19 13:25:33 -07:00
0135e2751e versioning: ipam config is not versioned 2016-09-19 13:25:28 -07:00
04ba7c7761 versioning: plugins require version match with config
infer version 0.1.0 when config is missing an explicit "cniVersion" field
2016-09-19 13:00:49 -07:00
fba37620e0 versioning: plugins require version match with config
infer version 0.1.0 when config is missing an explicit "cniVersion" field
2016-09-19 13:00:49 -07:00
1e4e8fdb5d versioning: add basic version decode for network config 2016-09-19 12:57:08 -07:00
64bbcd8cf7 versioning: add basic version decode for network config 2016-09-19 12:57:08 -07:00
75d983ec25 README: List cilium as 3rd party plugin
Signed-off-by: Thomas Graf <tgraf@suug.ch>
2016-09-19 13:45:39 +02:00
921292ff77 skel: Plugins require a cniVersion in the NetConf 2016-09-18 21:30:57 -07:00
fd150a4c97 skel: Plugins require a cniVersion in the NetConf 2016-09-18 21:30:57 -07:00
56032390fe Merge pull request #287 from rosenhouse/multi-version
Plugins report a list of supported versions
2016-09-12 13:44:14 -07:00
3f4011b3ec Merge pull request #295 from rosenhouse/update-readme-cloudfoundry-link
readme: update link to CloudFoundry usage of CNI
2016-09-08 00:47:04 -04:00
d32ccb1899 readme: update link to CloudFoundry usage of CNI 2016-09-06 23:48:48 -04:00
de91f31ae7 versioning: revert spec version to 0.2.0 2016-09-06 15:37:42 -04:00
7958b9f0cc versioning: revert spec version to 0.2.0 2016-09-06 15:37:42 -04:00
bf6948da19 versioning: misc cleanups
highlights:
 - NetConf struct finally includes cniVersion field
 - improve test coverage of current version report behavior
 - godoc a few key functions
 - allow tests to control version list reported by no-op plugin
2016-09-06 15:35:58 -04:00
d5e2e375d4 versioning: misc cleanups
highlights:
 - NetConf struct finally includes cniVersion field
 - improve test coverage of current version report behavior
 - godoc a few key functions
 - allow tests to control version list reported by no-op plugin
2016-09-06 15:35:58 -04:00
07a8a28637 Merge pull request #294 from dcbw/host-local-ranges
Fix handling of host-local IP address ranges
2016-09-05 23:24:42 -04:00
95a9ea0bd2 host-local: don't allocate the broadcast address or allow invalid networks
There aren't any IPs to allocate in /32 or /31 networks, so don't allow them.
2016-09-02 17:20:09 -05:00
959af1e6ab host-local: ensure requested IPs are within the given range
And also make sure that RangeStart and RangeEnd are sane.
2016-09-02 16:52:29 -05:00
dfc4f7cd2a host-local: don't allocate past RangeEnd
When RangeEnd is given, a.end = RangeEnd+1.

If when getSearchRange() is called and lastReservedIP equals
RangeEnd, a.nextIP() only compares lastReservedIP (which in this
example is RangeEnd) against a.end (which in this example is
RangeEnd+1) and they clearly don't match, so a.nextIP() returns
start=RangeEnd+1 and end=RangeEnd.

Get() happily allocates RangeEnd+1 because it only compares 'cur'
to the end returned by getSearchRange(), not to a.end, and thus
allocates past RangeEnd.

Since a.end is inclusive (eg, host-local will allocate a.end) the
fix is to simply set a.end equal to RangeEnd.
2016-09-02 16:37:34 -05:00
2f97407396 invoke: better name and unit test coverage for GetVersionInfo 2016-09-02 16:39:01 -04:00
bf31ed1591 invoke: better name and unit test coverage for GetVersionInfo 2016-09-02 16:39:01 -04:00
f467134838 invoke: correctly infer version for 0.1.0-vintage plugins
Older plugins return a known error when issued the VERSION command.
Capture this error and report it as a 0.1.0 version plugin.
2016-09-02 16:02:07 -04:00
97192fc979 invoke: correctly infer version for 0.1.0-vintage plugins
Older plugins return a known error when issued the VERSION command.
Capture this error and report it as a 0.1.0 version plugin.
2016-09-02 16:02:07 -04:00
2142c076ab versioning: adds tooling to compile a program against a given old CNI version
Allows us to write tests that cover interactions between components of
differing versions
2016-09-02 16:02:02 -04:00
deb4466041 versioning: adds tooling to compile a program against a given old CNI version
Allows us to write tests that cover interactions between components of
differing versions
2016-09-02 16:02:02 -04:00
bd3ade0c54 pkg/invoke: refactor plugin exec and backfill unit tests 2016-09-02 15:59:25 -04:00
dea1c6e44d pkg/invoke: refactor plugin exec and backfill unit tests 2016-09-02 15:59:25 -04:00
383c84031e versioning: document meaning of 'Legacy' version support 2016-09-02 11:26:17 -04:00
adf28a84c6 versioning: document meaning of 'Legacy' version support 2016-09-02 11:26:17 -04:00
b1e254d901 versioning: plugins report a list of supported versions
Further progress on versioning support (Issue #266).
Bump CNI spec version to 0.3.0
2016-09-02 11:26:17 -04:00
536cb5b99b versioning: plugins report a list of supported versions
Further progress on versioning support (Issue #266).
Bump CNI spec version to 0.3.0
2016-09-02 11:26:17 -04:00
c5e39a87f7 Merge pull request #293 from rosenhouse/backfill-invoke-exec-tests
Backfill tests for plugin execution
2016-08-31 20:49:08 -04:00
63ace9e496 Merge pull request #293 from rosenhouse/backfill-invoke-exec-tests
Backfill tests for plugin execution
2016-08-31 20:49:08 -04:00
e0ea82b229 Merge pull request #290 from asridharan/dcos
Fixed the check for network namespace path.
2016-08-31 18:19:02 -05:00
26241a517a Merge pull request #290 from asridharan/dcos
Fixed the check for network namespace path.
2016-08-31 18:19:02 -05:00
7281d5792a pkg/ns: fixed the check for network namespace path.
The expectation on older kernels (< 3.19) was to have the network
namespace always be a directory. This is not true if the network
namespace is bind mounted to a file, and will make the plugin fail
erroneously in such cases. The fix is to remove this assumption
completely and just do a basic check on the file system types being
returned.

Fixes #288
2016-08-31 21:41:00 +00:00
1ce5e54e99 pkg/ns: fixed the check for network namespace path.
The expectation on older kernels (< 3.19) was to have the network
namespace always be a directory. This is not true if the network
namespace is bind mounted to a file, and will make the plugin fail
erroneously in such cases. The fix is to remove this assumption
completely and just do a basic check on the file system types being
returned.

Fixes #288
2016-08-31 21:41:00 +00:00
4d77bc177c Merge pull request #291 from tomdee/SetupVeth
pkg/ip: Ensure that SetupVeth returns correct hostVeth
2016-08-31 16:32:09 -05:00
31ef82276a pkg/ip: Ensure that SetupVeth returns correct hostVeth
The veth is moved from the container NS to the host NS.
This is handled by the code that sets the link to UP but the wrong
hostVeth is returned to the calling code.
2016-08-31 14:17:25 -07:00
726c6b4578 pkg/ip: Ensure that SetupVeth returns correct hostVeth
The veth is moved from the container NS to the host NS.
This is handled by the code that sets the link to UP but the wrong
hostVeth is returned to the calling code.
2016-08-31 14:17:25 -07:00
d3ecadb860 invoke: backfill tests for plugin execution 2016-08-30 23:52:47 -04:00
0e675fa14f invoke: backfill tests for plugin execution 2016-08-30 23:52:47 -04:00
9d5e6e60e7 Merge pull request #241 from luxas/add_platforms
Add arm64 and ppc64le as new platforms
2016-08-13 15:11:28 -07:00
39f8436a8b release: Release binaries for arm, arm64 and ppc64le too
Modify the releasing script to cross-compile for the new architectures, but also keep backwards-compability

ref #209
2016-08-13 12:45:04 +03:00
21d25959ee travis: Cross-compile for arm, arm64 and ppc64le on every build
Cross-compile cni for arm, arm64 and ppc64le with go1.6 only
Allow go tip to fail
Set fast_finish to true, which means travis will instantly return build failure when any of the required builds fail

ref #209
2016-08-13 12:44:45 +03:00
fc58b79b45 build: Update the build script to make it possible to build for other architectures
This makes it possible to cross-compile cni like so:
$ GOARCH=arm ./build
$ GOARCH=arm64 ./build
$ GOARCH=ppc64le ./build

ref #209
2016-08-13 12:44:28 +03:00
488db88fd1 vendor: Bump golang.org/x/sys/unix to a newer version
The current vendor of sys/unix is really old, and doesn't work on arm64 and ppc64le
Updating to the latest version might also fix other issues

ref #209
2016-08-13 12:44:04 +03:00
787d59ab5d Merge pull request #267 from rosenhouse/version-cmd
Adds VERSION command
2016-08-12 15:06:22 -07:00
415d707faf Merge pull request #267 from rosenhouse/version-cmd
Adds VERSION command
2016-08-12 15:06:22 -07:00
349d66d51c Merge pull request #267 from rosenhouse/version-cmd
Adds VERSION command
2016-08-12 15:06:22 -07:00
5a67f5d6fb Merge pull request #273 from zreigz/reconfigure-bridge-ip
Reconfigure bridge IP address
2016-08-12 15:04:22 -07:00
60bb795c2e Merge pull request #274 from lukasredynk/update_netlink
Update of netlink version
2016-08-12 13:07:26 -07:00
113dfd61bd Merge pull request #280 from prateekgogia/bug-fix-155
pkg/ip: Return correct error if container name provided exists, and test cases
2016-08-12 13:04:49 -07:00
3c1d4581c4 pkg/ip: Return correct error if container name provided exists, and test cases
If interface name for a container provided by a user is already present,
Veth creation fails with incorrect error.
If os.IsExist error is returned by makeVethPair:
* Check for peer name, if exists generate another random peer name,
* else, IsExist error is due to container interface present, return error.

Fixes #155
2016-08-11 18:51:03 +00:00
ad8a052dd3 pkg/ip: Return correct error if container name provided exists, and test cases
If interface name for a container provided by a user is already present,
Veth creation fails with incorrect error.
If os.IsExist error is returned by makeVethPair:
* Check for peer name, if exists generate another random peer name,
* else, IsExist error is due to container interface present, return error.

Fixes #155
2016-08-11 18:51:03 +00:00
8cb3a9323a libcni: add util function InjectConf 2016-08-08 13:21:25 +02:00
7c579af789 Merge pull request #277 from steveeJ/test-allocator-reserveip
ipam/host-local/allocator tests: cover requested IP
2016-08-02 14:43:43 -07:00
08529639f4 documentation: add description for forceAddress parameter 2016-08-02 07:33:44 +02:00
ee64ac74bc documentation: add description for forceAddress parameter 2016-08-02 07:33:44 +02:00
d09b18dac4 plugins: reconfigure bridge IP address
Add possibility to reconfigure bridge IP address when there is a new value.
New boolean flag added to net configuration to force IP change if it is need.
Otherwise code behaves as previously and throws error
2016-08-02 07:33:17 +02:00
054fa9e42d ipam/host-local/allocator tests: cover requested IP
Further:
* improve error handling
2016-08-01 18:13:15 -07:00
f5ead7969a Merge pull request #254 from steveeJ/ip-based-macaddr
pkg/ipam: IP based macaddr
2016-08-01 17:43:49 -07:00
22a0ddc906 pkg/utils/hwaddr tests: cover v4 in v6 addr 2016-08-01 16:50:40 -07:00
c0ac3913a1 pkg/utils/hwaddr tests: cover v4 in v6 addr 2016-08-01 16:50:40 -07:00
35ce5d68e0 Update of netlink version
This is required for
https://github.com/containernetworking/cni/issues/251 because version of
netlink used doesn't support creating tap devices.
2016-08-01 14:04:03 +02:00
c1ff202179 Merge pull request #272 from rosenhouse/types-of-users
README: distinguish between runtimes and plugins
2016-07-25 16:35:58 -07:00
5a52316ab5 plugins/{bridge,macvlan} test: ensure hardware addr
* bridge: Test the following interface's hardware address for the CNI specific
prefix:
  - bridge with IP address
  - container veth
* plugins/macvlan test: ensure hardware addr
2016-07-22 15:40:00 -07:00
00f27577d5 pkg/ip link_test: ensure SetHWAddrByIP has an effect 2016-07-22 15:34:54 -07:00
f68cea27bc pkg/ip link_test: ensure SetHWAddrByIP has an effect 2016-07-22 15:34:54 -07:00
55fd81f775 plugins/ptp: set the host veth hwaddr correctly 2016-07-22 15:34:54 -07:00
c317888ac5 pkg/ip: use iface name in SetHWAddrByIP 2016-07-22 15:34:53 -07:00
7d19c01818 pkg/ip: use iface name in SetHWAddrByIP 2016-07-22 15:34:53 -07:00
1a04d9abba pkg/ip tests: cover SetupVeth and DelLinkByName* 2016-07-22 15:34:53 -07:00
8e1c215116 pkg/ip tests: cover SetupVeth and DelLinkByName* 2016-07-22 15:34:53 -07:00
b6137de993 plugins: set MAC addresses based on IP
This will give deterministic MAC addresses for all interfaces CNI
creates and manages the IP for:
* bridge: container veth and host bridge
* macvlan: container veth
* ptp: container veth and host veth
2016-07-22 15:34:53 -07:00
924b30b57d plugins: set MAC addresses based on IP
This will give deterministic MAC addresses for all interfaces CNI
creates and manages the IP for:
* bridge: container veth and host bridge
* macvlan: container veth
* ptp: container veth and host veth
2016-07-22 15:34:53 -07:00
c3e8563e77 pkg/utils/hwaddr: migrate code from IPAM pkg 2016-07-22 15:18:38 -07:00
3a1354cff6 pkg/utils/hwaddr: migrate code from IPAM pkg 2016-07-22 15:18:38 -07:00
7ea07a6405 pkg/ipam{,test}: improve error handling and tests
* _suite.go and _test.go file should be in the same package, using the
  _test package for that, which requires some fields and methods to be
  exported
* Introduce error type for cleaner error handling
* test adaptions for error type checking
2016-07-22 15:17:22 -07:00
96867eae05 pkg/ipam{,test}: improve error handling and tests
* _suite.go and _test.go file should be in the same package, using the
  _test package for that, which requires some fields and methods to be
  exported
* Introduce error type for cleaner error handling
* test adaptions for error type checking
2016-07-22 15:17:22 -07:00
75f1fa2260 add tests for generateHardwardAddr 2016-07-22 15:01:57 -07:00
1c1424c472 add tests for generateHardwardAddr 2016-07-22 15:01:57 -07:00
c19b82efc2 configure mac address based on assigned ip 2016-07-22 14:57:52 -07:00
b2e53181d1 configure mac address based on assigned ip 2016-07-22 14:57:52 -07:00
39a8fe4766 Merge pull request #270 from rosenhouse/integration-tests
Add integration tests of libcni
2016-07-22 13:36:20 -07:00
c657c61767 plugins: cleanup tests of no-op plugin 2016-07-22 13:30:55 -07:00
424a8b38cf README: distinguish between runtimes and plugins
Adds a link to the 3rd party SR-IOV plugin.
2016-07-22 13:22:35 -07:00
04fbde4ce8 docs: fix tuning doc, correct the result json
it does not report the version, it never did
2016-07-21 13:06:05 -07:00
dfd2c142ae docs: fix tuning doc, correct the result json
it does not report the version, it never did
2016-07-21 13:06:05 -07:00
7bc96f05bb spec: add version command and increment version to 0.2.0 2016-07-21 12:59:08 -07:00
0506bae8fc spec: remove CNI_VERSION env var, was never been implemented
No one uses this, and it is causing confusion (see #150)
2016-07-20 13:39:51 -07:00
bacdc3668d libcni: add integration test coverage 2016-07-15 13:01:08 -07:00
6496749565 plugins: adds new no-op plugin that may be used as a test-double
Plugin can be configured to record all inputs and to respond with
arbitrary stdout or error message.  Will support upcoming integration
testing.
2016-07-15 12:48:02 -07:00
5835c2bbb1 plugins: adds new no-op plugin that may be used as a test-double
Plugin can be configured to record all inputs and to respond with
arbitrary stdout or error message.  Will support upcoming integration
testing.
2016-07-15 12:48:02 -07:00
7f098f7c4a Merge pull request #269 from rosenhouse/skel-dependency-injection
Refactor skel with dependency injection
2016-07-15 12:45:18 -07:00
728071e7d1 misc: fix up copyright dates 2016-07-14 16:09:27 -07:00
a2aff8c6a8 misc: fix up copyright dates 2016-07-14 16:09:27 -07:00
f484990a00 Merge pull request #263 from feiskyer/ptp-test
plugins/main/ptp: add functional tests
2016-07-14 14:32:05 -06:00
30c99d17cc pkg/skel: plugins now respond to VERSION command
To support CNI spec versioning, plugins must be able to report version
information to container runtimes.
2016-07-14 00:24:32 -04:00
63755d7c57 pkg/skel: plugins now respond to VERSION command
To support CNI spec versioning, plugins must be able to report version
information to container runtimes.
2016-07-14 00:24:32 -04:00
eda79f7645 pkg/skel: improve error message for missing CNI_COMMAND env var
This makes the error message for missing CNI_COMMAND consistent with
that of other required environment variables.
2016-07-14 00:24:32 -04:00
f436418525 pkg/skel: improve error message for missing CNI_COMMAND env var
This makes the error message for missing CNI_COMMAND consistent with
that of other required environment variables.
2016-07-14 00:24:32 -04:00
f2b6ec0375 pkg/skel: missing env var log lines appear in stderr
Previously, the log lines appeared in stdout before the JSON encoding of
the error message.  That would break JSON parsing of stdout.  Instead, we use
stderr for these unstructured logs, consistent with the CNI spec.
2016-07-14 00:24:27 -04:00
c17e700759 pkg/skel: missing env var log lines appear in stderr
Previously, the log lines appeared in stdout before the JSON encoding of
the error message.  That would break JSON parsing of stdout.  Instead, we use
stderr for these unstructured logs, consistent with the CNI spec.
2016-07-14 00:24:27 -04:00
3c43314926 pkg/skel: refactor to use dependency injection
Extract dependencies on os to enable more complete unit test coverage
2016-07-14 00:06:58 -04:00
791d259e55 pkg/skel: refactor to use dependency injection
Extract dependencies on os to enable more complete unit test coverage
2016-07-14 00:06:58 -04:00
cd14fc0e06 plugins/main/ptp: add functional tests 2016-07-11 09:25:36 +08:00
8a93673209 Merge pull request #264 from rosenhouse/test-with-vagrant
Add Vagrantfile, document how to run test suite in a vagrant VM
2016-07-06 11:33:32 -07:00
0c2b5f5139 Add Vagrantfile, document how to run test suite in a vagrant VM 2016-07-05 23:12:02 -07:00
b8e92ed030 Merge pull request #247 from tomdee/spec-args-field
SPEC: introduce "args" field and new error code
2016-06-14 15:31:23 -07:00
7b381a1af2 SPEC: introduce "args" field and new error code
Based on previous discussions on the CNI maintainers calls, the spec is
unclear on 1) when CNI_ARGS should be used and 2) the fact the dynamic
config can be passed in through the network JSON.

This PR makes it clear that per-container config can be passed in
through the network JSON, adding a top level `args` field into
which orchestrators can add additional metadata without worrying that
plugins might reject the additional data. It also allows for plugins to
reject unknown fields passed in at the top level.

Using JSON is preferable to CNI_ARGS since it allows namespaced and
structured data. CNI_ARGS is a flat list of KV pairs which has reserved
characters with no escaping rules defined.
CNI_ARGS may still be used by orchestrators that want the simplicity of
passing the network config JSON as specified by the user, unchanged
through to the CNI plugin. But for any kind of structured data, it's
recommended that the `args` field in the JSON is used instead.
2016-06-14 15:02:51 -07:00
4666a24b6d README: grammatical corrections 2016-06-14 09:54:15 -07:00
bd341ab5d1 pkg/ns: fix misspelling in comment 2016-06-14 09:52:58 -07:00
0136a73037 pkg/ns: fix misspelling in comment 2016-06-14 09:52:58 -07:00
3bee295ba5 Merge pull request #240 from steveeJ/unmarshall-string
pkg/types: unmarshal string tests
2016-06-13 18:30:24 -07:00
d48cb53715 pkg/types: cover string for unmarshal tests 2016-06-13 18:18:36 -07:00
3476000725 pkg/types: cover string for unmarshal tests 2016-06-13 18:18:36 -07:00
17dcf87589 Merge pull request #238 from tomdee/string-unmarshaller
pkg/types: Add UnmarshallableString type
2016-06-13 18:17:48 -07:00
c3c0803817 Merge pull request #243 from containernetworking/maintainers-update-email
MAINTAINERS: Update @tomdee email address
2016-06-10 01:41:42 +02:00
0ffad38994 MAINTAINERS: Update @tomdee email address 2016-06-09 16:32:26 -07:00
efc0f2987f pkg/types: Add UnmarshallableString type
Allow strings to be unmarshalled for CNI_ARGS

CNI_ARGS uses types.LoadArgs to populate a struct.
The fields in the struct must meet the TextUnmarshaler interface.

This code adds a UnmarshallableString type to assist with this.
2016-06-09 14:45:58 -07:00
031567c216 pkg/types: Add UnmarshallableString type
Allow strings to be unmarshalled for CNI_ARGS

CNI_ARGS uses types.LoadArgs to populate a struct.
The fields in the struct must meet the TextUnmarshaler interface.

This code adds a UnmarshallableString type to assist with this.
2016-06-09 14:45:58 -07:00
c6864e1060 Merge pull request #229 from steveeJ/cleanup-travis
travis: cleanup and bump go versions
2016-06-02 20:43:11 +02:00
2445a960a9 plugins/ipam: round robin ip allocation for host-local ipam
This changes the ip allocation logic to round robin. Before this, host-local IPAM searched for available IPs from start of subnet. Hence it tends to allocate IPs that had been used recently. This is not ideal since it may cause collisions.
2016-06-02 20:37:05 +02:00
3a4124d257 pkg/ns: introduce error types indicate NS verification 2016-05-27 13:50:16 +02:00
cfedcfd8f2 skel/test: add case for empty NETNS 2016-05-27 12:26:42 +02:00
5fe036b88a plugins: don't require CNI_NETNS for DEL command
This will allow to free up the IPAM allocations when the caller doesn't
have access to the network namespace anymore, e.g. due to a reboot.
2016-05-27 10:57:39 +02:00
d7de8d4f98 pkg/skel: allow arg requriements specified by CMD 2016-05-27 10:56:24 +02:00
3b5d7a3bb0 travis: use go1.5.4 2016-05-26 15:33:58 +02:00
572fcc7076 travis: cleanup and bump go versions
* cleanup structure
* explicitly use go-{1.5.3,1.6.2}
2016-05-26 14:43:18 +02:00
2d47b0396d pkg/ns: consider PROCFS during NS verification
This is an attempt to bring compatibility with Kernel <3.19, where NSFS
where PROCFS was used for network namespaces.
2016-05-26 12:42:50 +02:00
ea073fc4c7 pkg/ns: test IsNSFS() 2016-05-24 22:30:49 +02:00
f64cfb84f8 pkg/ns: test case for rejecting a non-ns nspath 2016-05-24 22:30:49 +02:00
39eace3c38 pkg/ns: verify netns when initialized with GetNS 2016-05-24 22:30:49 +02:00
a8d1029294 pkg/ns: add tests cases for Close()'d NS 2016-05-24 21:15:51 +02:00
40440ec887 pkg/ns: don't allow operations after Close() 2016-05-24 20:52:00 +02:00
8814631f80 bridge: add isDefaultGateway field
When isDefaultGateway is true it automatically sets isGateway to true.
The default route will be added via the (bridge's) gateway IP.
If a default gateway has been configured via IPAM in the same
configuration file, the plugin will error out.
2016-05-21 00:38:42 +02:00
fce81fbf78 testutils: add e2e testing helper code 2016-05-20 17:10:25 -05:00
f2922c5c1f ns: add interface, use it, and fix thread-related namespace switch issues
Add a namespace object interface for somewhat cleaner code when
creating and switching between network namespaces.  All created
namespaces are now mounted in /var/run/netns to ensure they
have persistent inodes and paths that can be passed around
between plugin components without relying on the current namespace
being correct.

Also remove the thread-locking arguments from the ns package
per https://github.com/appc/cni/issues/183 by doing all the namespace
changes in a separate goroutine that locks/unlocks itself, instead of
the caller having to track OS thread locking.
2016-05-20 17:10:25 -05:00
c019ed25d2 Merge pull request #199 from dcbw/fix-bridge-tx-queue-length
bridge: leave TX queue length as kernel default, not 0
2016-05-19 23:05:08 +02:00
3738692372 Merge pull request #200 from squaremo/simplify-readme
Simplify README preamble
2016-05-19 13:21:40 +01:00
0bd4bafcda Merge pull request #175. 2016-05-19 12:08:41 +02:00
734bf1ba5b pkg/ns: evaluate syscall number at compile-time
Previously this code used a run-time map lookup keyed by
runtime.GOOS/GOARCH.  This version uses conditional compilation to make
this choice at compile time, giving immediate feedback for unsupported
platforms.
2016-05-19 12:06:37 +02:00
5de15979c6 plugins/bridge: add support to set hairpin mode 2016-05-19 12:06:37 +02:00
5978cf8b88 pkg/ns: use correct syscall number on arm 2016-05-19 12:06:37 +02:00
b34210aa1c *: appc/cni -> containernetworking/cni
The project has been moved so internally we simply rename everything.
Consumers are recommended to update their vendored version of cni.
2016-05-19 12:06:36 +02:00
fc229c5090 *: appc/cni -> containernetworking/cni
The project has been moved so internally we simply rename everything.
Consumers are recommended to update their vendored version of cni.
2016-05-19 12:06:36 +02:00
5cbd217cbe pkg/utils: add functions to work with sysctl 2016-05-19 12:06:16 +02:00
19850efc3a Document use of goroutine and lockosthread in test helpers 2016-05-19 12:06:16 +02:00
5158edacef ns: fix reading net namespace in multi-threaded processes
/proc/self/ns/net gives the main thread's namespace, not necessarily
the namespace of the thread that's running the testcases.  This causes
sporadic failures of the tests.

For example, with a testcase reading inodes after switching netns:

/proc/27686/task/27689/ns/net 4026532565
/proc/self/ns/net 4026531969
/proc/27686/task/27689/ns/net 4026532565

See also:
008d17ae00

Running Suite: pkg/ns Suite
===========================
Random Seed: 1459953577
Will run 6 of 6 specs

• Failure [0.028 seconds]
Linux namespace operations
/cni/gopath/src/github.com/appc/cni/pkg/ns/ns_test.go:167
  WithNetNS
  /cni/gopath/src/github.com/appc/cni/pkg/ns/ns_test.go:166
    executes the callback within the target network namespace [It]
    /cni/gopath/src/github.com/appc/cni/pkg/ns/ns_test.go:97

    Expected
        <uint64>: 4026531969
    to equal
        <uint64>: 4026532565

    /cni/gopath/src/github.com/appc/cni/pkg/ns/ns_test.go:96
------------------------------
•••••

Summarizing 1 Failure:

[Fail] Linux namespace operations WithNetNS [It] executes the callback within the target network namespace
/cni/gopath/src/github.com/appc/cni/pkg/ns/ns_test.go:96

Ran 6 of 6 Specs in 0.564 seconds
FAIL! -- 5 Passed | 1 Failed | 0 Pending | 0 Skipped --- FAIL: TestNs (0.56s)
FAIL
2016-05-19 12:06:16 +02:00
5260862f80 Fix issues with MakeNetworkNS test helper 2016-05-19 12:06:16 +02:00
c77f7431fb Add basic unit tests of testhelpers 2016-05-19 12:06:16 +02:00
bd7b83dd98 Extract inode inspection functions into testhelpers 2016-05-19 12:06:16 +02:00
3358f16dd6 Extract testhelpers from loopback test suite 2016-05-19 12:06:16 +02:00
9079565e0d *: appc/cni -> containernetworking/cni
The project has been moved so internally we simply rename everything.
Consumers are recommended to update their vendored version of cni.
2016-05-06 16:40:27 +02:00
680d1f6ec2 *: appc/cni -> containernetworking/cni
The project has been moved so internally we simply rename everything.
Consumers are recommended to update their vendored version of cni.
2016-05-06 16:40:27 +02:00
c3a90e7682 Merge pull request #187 from appc/v0.3.0
merge unneeded branch v0.3.0 into master
2016-04-22 19:25:54 +02:00
f73261c431 Merge pull request #182 from rosenhouse/document-lockosthread
Document use of goroutine and LockOSThread in test helpers
2016-04-18 10:45:46 -07:00
c272c49555 ns: fix reading net namespace in multi-threaded processes
/proc/self/ns/net gives the main thread's namespace, not necessarily
the namespace of the thread that's running the testcases.  This causes
sporadic failures of the tests.

For example, with a testcase reading inodes after switching netns:

/proc/27686/task/27689/ns/net 4026532565
/proc/self/ns/net 4026531969
/proc/27686/task/27689/ns/net 4026532565

See also:
008d17ae00

Running Suite: pkg/ns Suite
===========================
Random Seed: 1459953577
Will run 6 of 6 specs

• Failure [0.028 seconds]
Linux namespace operations
/cni/gopath/src/github.com/appc/cni/pkg/ns/ns_test.go:167
  WithNetNS
  /cni/gopath/src/github.com/appc/cni/pkg/ns/ns_test.go:166
    executes the callback within the target network namespace [It]
    /cni/gopath/src/github.com/appc/cni/pkg/ns/ns_test.go:97

    Expected
        <uint64>: 4026531969
    to equal
        <uint64>: 4026532565

    /cni/gopath/src/github.com/appc/cni/pkg/ns/ns_test.go:96
------------------------------
•••••

Summarizing 1 Failure:

[Fail] Linux namespace operations WithNetNS [It] executes the callback within the target network namespace
/cni/gopath/src/github.com/appc/cni/pkg/ns/ns_test.go:96

Ran 6 of 6 Specs in 0.564 seconds
FAIL! -- 5 Passed | 1 Failed | 0 Pending | 0 Skipped --- FAIL: TestNs (0.56s)
FAIL
2016-04-18 10:35:23 -05:00
d701ca6c82 Document use of goroutine and lockosthread in test helpers 2016-04-17 20:27:02 -07:00
c085ec98fd Fix issues with MakeNetworkNS test helper 2016-04-17 19:47:54 -07:00
82851a860e Add basic unit tests of testhelpers 2016-04-17 19:25:39 -07:00
5f757b6af7 Extract inode inspection functions into testhelpers 2016-04-17 18:35:49 -07:00
4a79ac4cda Extract testhelpers from loopback test suite 2016-04-17 18:28:10 -07:00
e1bc41a42a pkg/utils: add functions to work with sysctl 2016-04-07 21:40:51 +02:00
11c0bfada8 *: add missing license headers + check 2016-04-01 15:35:21 +02:00
2c1633f8e9 pkg/utils: correct the test's expected chain names 2016-03-31 18:01:32 +02:00
6c9b5a361a *: add comment to iptables rules for ipmasq 2016-03-31 18:01:32 +02:00
42b74b9eb5 pkg/utils: remove unneeded condition in tests 2016-03-31 18:01:32 +02:00
9612d36615 pkg/utils: fix docstring 2016-03-31 18:01:32 +02:00
d4e088aad4 pkg/utils: split and unexport constants 2016-03-31 18:01:32 +02:00
f429750105 pkg/utils: fix docstring 2016-03-31 18:01:32 +02:00
f96a716154 pkg/utils: use constant for chain prefix 2016-03-31 18:01:32 +02:00
b4a2a1fa51 pkg/utils: use name+id for hash and extend tests 2016-03-31 18:01:32 +02:00
b87cf1ba9c pkg: add a function to generate chain names
Adds a function to generate chain names for use in iptables and
ports all drivers to use that function. Also adds tests for the
said function.
2016-03-31 18:01:32 +02:00
dc44feb5b5 pkg/types: add tests for args 2016-03-21 20:40:39 +01:00
fc83c24eb5 pkg/types: add IgnoreUnknown arg and logic
This commit adds a struct type CommonArgs that is to be embedded in
every plugin's argument struct. It contains a field named
"IgnoreUnknown" which will be parsed as a boolean and can be provided to
ignore unknown arguments passed to the plugin.
2016-03-21 20:36:05 +01:00
8cc424c5aa pkg/skel: add rudimentary unit tests
This is an attempt to testing the PluginMain() function of the skel pkg.
We should be able to do better by using a mockable interface for the
plugins, but this is a start.
2016-03-18 22:34:29 +01:00
ca978caee7 Update docstring on WithNetNS 2016-03-15 08:37:00 -07:00
b99854d124 WithNetNS restores original namespace when callback errors
- adds test coverage of WithNetNS in BDD-style
2016-03-15 01:51:58 -07:00
05683bf11d Add invoke.Delegate{Add,Del} for use by meta-plugins
The 'flannel' meta plugin delegates to other plugins to do the actual
OS-level work. It used the ipam.Exec{Add,Del} procedures for this
delegation, since those do precisely what's needed.

However this is a bit misleading, since the flannel plugin _isn't_
doing this for IPAM, and the ipam.Exec* procedures aren't doing
something specific to IPAM plugins.

So: anticipating that there may be more meta plugins that want to
delegate in the same way, this commit moves generic delegation
procedures to `pkg/invoke`, and makes the `pkg/ipam` procedures (still
used, accurately, in the non-meta plugins) shims.
2016-03-04 11:01:22 +00:00
e2673df403 Merge pull request #120 from zachgersh/find-better-error
Better error messages when plugin is not found
2016-03-02 11:14:50 +01:00
d2af995349 added the String method to Result type
Signed-off-by: André Martins <aanm90@gmail.com>
2016-02-19 17:40:46 +00:00
0b6ad716e8 new plugin: tuning
Allow users to tune net network parameters such as somaxconn.

With this patch, users can add a new network configuration:

> {
>   "name": "mytuning",
>   "type": "tuning",
>   "sysctl": {
>           "net.core.somaxconn": "500"
>   }
> }

The value /proc/sys/net/core/somaxconn will be set to 500 in the network
namespace but will remain unchanged on the host.

Only sysctl parameters that belong to the network subsystem can be
modified.

Related to: https://github.com/coreos/rkt/pull/2140
2016-02-18 15:29:12 +01:00
3382b459a7 Better error message when plugin cannot be found 2016-02-10 13:42:10 -08:00
a08b015fa5 *: reflect SPEC's DNS changes in implementation
* DNS is now a type which will result in a JSON dictionary in
  configurations and results
* Minor refactoring, making use of type embedding
2016-01-29 10:39:28 +01:00
c7db40fb4c docs/ptp: update example and DNS description 2016-01-29 10:39:28 +01:00
3bbc43f24d *: add "dns" field to the configuration
appc/cni#76 added a "dns" field in the result JSON. But before this
patch, the plugins had no way of knowing which name server to return.

There could be two ways of knowing which name server to return:
1. add it as an extra argument ("CNI_ARGS")
2. add it in the network configuration as a convenience (received via
   stdin)

I chose the second way because it is easier. In the case of rkt, it
means the user could just add the DNS name servers in
/etc/rkt/net.d/mynetwork.conf.
2016-01-26 18:54:56 +01:00
cf3c027960 *: add "dns" field to the configuration
appc/cni#76 added a "dns" field in the result JSON. But before this
patch, the plugins had no way of knowing which name server to return.

There could be two ways of knowing which name server to return:
1. add it as an extra argument ("CNI_ARGS")
2. add it in the network configuration as a convenience (received via
   stdin)

I chose the second way because it is easier. In the case of rkt, it
means the user could just add the DNS name servers in
/etc/rkt/net.d/mynetwork.conf.
2016-01-26 18:54:56 +01:00
6b58eda23a pkg/ip: fix typo in error message, s/iptabes/iptables/ 2015-12-16 13:20:19 -08:00
dd719a43ce flannel: doc fix and check for missing subnet.env fields
- Document that flannel outputs FLANNEL_NETWORK b/c we use it
- Check and error out if expecited fields are missing in subnetEnv file

Fixes #72
2015-10-07 15:38:12 -07:00
4bee884fae Change copyright from CoreOS to CNI authors
CNI is developed by more than just CoreOS, Inc
and the copyright is retained by all CNI
contributors (for their respective contributed code).
2015-09-29 11:51:33 -07:00
4da892a669 link: switch to host netns to set up host veth end 2015-09-24 18:49:27 +02:00
8525fef466 Revert "plugins/ptp: allow host veth to be UP"
This reverts commit 231d2d5a27.
2015-09-24 18:32:18 +02:00
8d3eb91a37 plugins/ptp: allow host veth to be UP 2015-09-24 10:57:36 +02:00
4c57a90da6 update ptp docs to reflect changes in plugin 2015-09-24 09:43:55 +02:00
ae3ceedd69 bug fix: exec of DEL cmd caused JSON decode error
When plugin is executed with a DEL command, it does not
print result to stdout unless there is an error. Therefore
it stdout bytes should not be passed to json.Unmarshal.
2015-09-18 10:30:10 -07:00
09a8148e36 Factor an API out into a module
This takes some of the machinery from CNI and from the rkt networking
code, and turns it into a library that can be linked into go apps.

Included is an example command-line application that uses the library,
called `cnitool`.

Other headline changes:

 * Plugin exec'ing is factored out

The motivation here is to factor out the protocol for invoking
plugins. To that end, a generalisation of the code from api.go and
pkg/plugin/ipam.go goes into pkg/invoke/exec.go.

 * Move argument-handling and conf-loading into public API

The fact that the arguments get turned into an environment for the
plugin is incidental to the API; so, provide a way of supplying them
as a struct or saying "just use the same arguments as I got" (the
latter is for IPAM plugins).
2015-09-16 10:14:39 +01:00
104e8b483e plugin/ipam: correct formatting of error message 2015-09-08 18:08:15 +00:00
b5db5d3075 IPAM plugin: improve error messages
Make it more clear that we failed to find an IPAM plugin.
Check for a missing plugin name and issue a more helpful error.
2015-09-08 18:08:15 +00:00
ac7cd562fd plugin/ipam: fix typo in error message 2015-09-07 15:43:34 -07:00
169f8e0a52 CNI_ARGS: use ';' to split args as documented 2015-09-05 18:58:58 +02:00
a75401a13b host-local: allow ip request via CNI_ARGS
A specific IP can now be requested via the environment variable CNI_ARGS, e.g.
`CNI_ARGS=ip=1.2.3.4`.
The plugin will try to reserve the specified IP.
If this is not successful the execution will fail.
2015-09-04 01:38:22 +02:00
d1a0905345 host-local: allow ip request via CNI_ARGS
A specific IP can now be requested via the environment variable CNI_ARGS, e.g.
`CNI_ARGS=ip=1.2.3.4`.
The plugin will try to reserve the specified IP.
If this is not successful the execution will fail.
2015-09-04 01:38:22 +02:00
a41e1a699e Fix a few spelling mistakes in the docs 2015-08-20 16:41:25 +01:00
77a2e9727c Merge pull request #34 from eyakubovich/document-plugins
Document the plugins
2015-07-22 09:02:39 -07:00
f741bc324b Rename veth plugin to ptp and add docs.
ptp (point-to-point) better describes the nature of the plugin
as veth pairs are also used by the bridge plugin.
2015-07-21 17:20:11 -07:00
507805f8c4 Document the plugins
Adds a .md file to the Documentation/ folder for each plugin.

Fixes #16
2015-07-21 17:19:18 -07:00
4f74b78041 enable net.ipv4.ip_forward in plugins that need it 2015-07-13 15:41:02 -07:00
6caffd48b3 No more path rewriting
Path rewriting causes too many problems when vendoring
vendored code. When CNI code is vendored into rkt,
godep has problems code already vendored by CNI.
2015-06-12 16:29:18 -07:00
58f6bc9c8b add flannel plugin
This introduces a notion of a "meta" plugin. A meta plugin
is one that delegates the actual work of setting up the interface
to the main plugin. The meta plugin is used to select and dynamically
configure the main plugin. The sequence of events, is as follows:

Given netconf like:

    {
	"name": "mynet",
	"type": "flannel",
	"delegate": {
	    "type": "bridge"
	}
    }

flannel fills in values like "mtu", "ipam.subnet" and delegates to
"bridge" main plugin. "bridge" plugin will operate as usual, calling
into ipam module for IP assignment.

Delegate dictionary should not contain "name" field as it will be
filled in by the flannel plugin.
2015-06-11 15:37:58 -07:00
31c0dbf110 Propagate json error object to the caller
When plugin errors out, it prints out a JSON object to stdout
describing the failure. This object needs to be propagated out
through the plugins and to the container runtime. This change
also adds Print method to both the result and error structs
for easy serialization to stdout.
2015-06-09 10:46:28 -07:00
a6a822268c add DHCP IPAM plugin
The plugin binary actually functions in two modes. The first mode
is a regular CNI plugin. The second mode (when stared with "daemon" arg)
runs a DHCP client daemon. When executed as a CNI plugin, it issues
an RPC request to the daemon for actual processing. The daemon is
required since a DHCP lease needs to be maintained by periodically
renewing it. One instance of the daemon can server arbitrary number
of containers/leases.
2015-05-21 13:36:51 -07:00
e59e2fd890 Merge pull request #9 from eyakubovich/errors
report errors via JSON
2015-05-08 15:08:03 -07:00
c7a1442830 report errors via JSON
This reflects the latest SPEC draft.
2015-05-08 15:07:09 -07:00
93be8e63f1 Do not use netns as ID or for entropy
ContainerID is now required so use that
or generate random bytes.

Fixes #5
2015-05-08 15:02:23 -07:00
c24708ff62 Add plugin code
This adds basic plugins.
"main" types: veth, bridge, macvlan
"ipam" type: host-local

The code has been ported over from github.com/coreos/rkt project
and adapted to fit the CNI spec.
2015-04-27 14:14:29 -07:00
1134 changed files with 275099 additions and 29076 deletions

29
.gitignore vendored
View File

@ -1,3 +1,30 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
bin/
gopath/
*.sw[ponm]
.vagrant
.idea
/release-*

View File

@ -1,29 +1,52 @@
language: go
sudo: required
dist: trusty
dist: xenial
go:
- 1.5.3
- 1.6
- tip
matrix:
allow_failures:
- go: tip
- 1.11.x
- 1.12.x
- 1.13.x
env:
global:
- TOOLS_CMD=golang.org/x/tools/cmd
- PATH=$GOROOT/bin:$PATH
- GO15VENDOREXPERIMENT=1
- PATH=$GOROOT/bin:$GOPATH/bin:$PATH
- CGO_ENABLED=0
matrix:
- TARGET=386
- TARGET=amd64
- TARGET=arm
- TARGET=arm64
- TARGET=ppc64le
- TARGET=s390x
- TARGET=mips64le
matrix:
fast_finish: true
include:
- os: windows
env: TARGET=amd64
go: 1.11.x
- os: windows
env: TARGET=amd64
go: 1.12.x
install:
- go get ${TOOLS_CMD}/cover
- go get github.com/modocache/gover
- go get github.com/mattn/goveralls
- go get github.com/onsi/ginkgo/ginkgo
- go get github.com/containernetworking/cni/cnitool
- go get golang.org/x/tools/cmd/cover
- go get github.com/modocache/gover
- go get github.com/mattn/goveralls
script:
- ./test
- |
if [ "${TARGET}" == "amd64" ]; then
GOARCH="${TARGET}" ./test_${TRAVIS_OS_NAME}.sh
else
GOARCH="${TARGET}" ./build_linux.sh
fi
notifications:
email: false
git:
depth: 9999999

View File

@ -26,9 +26,14 @@ are very busy and read the mailing lists.
## Getting Started
- Fork the repository on GitHub
- Read the [README](README.md) for build and test instructions
- Play with the project, submit bugs, submit pull requests!
## Building
Each plugin is compiled simply with `go build`. Two scripts, `build_linux.sh` and `build_windows.sh`,
are supplied which will build all the plugins for their respective OS.
## Contribution workflow
This is a rough outline of how to prepare a contribution:
@ -37,10 +42,47 @@ This is a rough outline of how to prepare a contribution:
- Make commits of logical units.
- Make sure your commit messages are in the proper format (see below).
- Push your changes to a topic branch in your fork of the repository.
- If you changed code, make sure the tests pass, and add any new tests as appropriate.
- Make sure any new code files have a license header.
- If you changed code:
- add automated tests to cover your changes, using the [Ginkgo](http://onsi.github.io/ginkgo/) & [Gomega](http://onsi.github.io/gomega/) style
- if the package did not previously have any test coverage, add it to the list
of `TESTABLE` packages in the `test.sh` script.
- run the full test script and ensure it passes
- Make sure any new code files have a license header (this is now enforced by automated tests)
- Submit a pull request to the original repository.
## How to run the test suite
We generally require test coverage of any new features or bug fixes.
Here's how you can run the test suite on any system (even Mac or Windows) using
[Vagrant](https://www.vagrantup.com/) and a hypervisor of your choice:
First, ensure that you have the [CNI repo](https://github.com/containernetworking/cni) and this repo (plugins) cloned side-by-side:
```bash
cd ~/workspace
git clone https://github.com/containernetworking/cni
git clone https://github.com/containernetworking/plugins
```
Next, boot the virtual machine and SSH in to run the tests:
```bash
cd ~/workspace/plugins
vagrant up
vagrant ssh
# you're now in a shell in a virtual machine
sudo su
go get github.com/onsi/ginkgo/ginkgo
go install github.com/containernetworking/cni/cnitool
cd /go/src/github.com/containernetworking/plugins
# to run the full test suite
./test_linux.sh
# to focus on a particular test suite
cd plugins/main/loopback
go test
```
# Acceptance policy
These things will make a PR more likely to be accepted:
@ -48,7 +90,7 @@ These things will make a PR more likely to be accepted:
* a well-described requirement
* tests for new code
* tests for old code!
* new code follows the conventions in old code
* new code and tests follow the conventions in old code and tests
* a good commit message (see below)
In general, we will merge a PR once two maintainers have endorsed it.
@ -84,3 +126,17 @@ The first line is the subject and should be no longer than 70 characters, the
second line is always blank, and other lines should be wrapped at 80 characters.
This allows the message to be easier to read on GitHub as well as in various
git tools.
## 3rd party plugins
So you've built a CNI plugin. Where should it live?
Short answer: We'd be happy to link to it from our [list of 3rd party plugins](README.md#3rd-party-plugins).
But we'd rather you kept the code in your own repo.
Long answer: An advantage of the CNI model is that independent plugins can be
built, distributed and used without any code changes to this repository. While
some widely used plugins (and a few less-popular legacy ones) live in this repo,
we're reluctant to add more.
If you have a good reason why the CNI maintainers should take custody of your
plugin, please open an issue or PR.

View File

@ -1,41 +0,0 @@
# host-local plugin
## Overview
host-local IPAM plugin allocates IPv4 addresses out of a specified address range.
It stores the state locally on the host filesystem, therefore ensuring uniqueness of IP addresses on a single host.
## Example configuration
```
{
"ipam": {
"type": "host-local",
"subnet": "10.10.0.0/16",
"rangeStart": "10.10.1.20",
"rangeEnd": "10.10.3.50",
"gateway": "10.10.0.254",
"routes": [
{ "dst": "0.0.0.0/0" },
{ "dst": "192.168.0.0/16", "gw": "10.10.5.1" }
]
}
}
```
## Network configuration reference
* `type` (string, required): "host-local".
* `subnet` (string, required): CIDR block to allocate out of.
* `rangeStart` (string, optional): IP inside of "subnet" from which to start allocating addresses. Defaults to ".2" IP inside of the "subnet" block.
* `rangeEnd` (string, optional): IP inside of "subnet" with which to end allocating addresses. Defaults to ".254" IP inside of the "subnet" block.
* `gateway` (string, optional): IP inside of "subnet" to designate as the gateway. Defaults to ".1" IP inside of the "subnet" block.
* `routes` (string, optional): list of routes to add to the container namespace. Each route is a dictionary with "dst" and optional "gw" fields. If "gw" is omitted, value of "gateway" will be used.
## Supported arguments
The following [CNI_ARGS](https://github.com/containernetworking/cni/blob/master/SPEC.md#parameters) are supported:
* `ip`: request a specific IP address from the subnet. If it's not available, the plugin will exit with an error
## Files
Allocated IP addresses are stored as files in /var/lib/cni/networks/$NETWORK_NAME.

View File

@ -1,36 +0,0 @@
# tuning plugin
## Overview
This plugin can change some system controls (sysctls) in the network namespace.
It does not create any network interfaces and therefore does not bring connectivity by itself.
It is only useful when used in addition to other plugins.
## Operation
The following network configuration file
```
{
"name": "mytuning",
"type": "tuning",
"sysctl": {
"net.core.somaxconn": "500"
}
}
```
will set /proc/sys/net/core/somaxconn to 500.
Other sysctls can be modified as long as they belong to the network namespace (`/proc/sys/net/*`).
A successful result would simply be:
```
{
"cniVersion": "0.1.0"
}
```
## Network sysctls documentation
Some network sysctls are documented in the Linux sources:
- [Documentation/sysctl/net.txt](https://www.kernel.org/doc/Documentation/sysctl/net.txt)
- [Documentation/networking/ip-sysctl.txt](https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt)
- [Documentation/networking/](https://www.kernel.org/doc/Documentation/networking/)

184
Godeps/Godeps.json generated
View File

@ -1,184 +0,0 @@
{
"ImportPath": "github.com/containernetworking/cni",
"GoVersion": "go1.6",
"Packages": [
"./..."
],
"Deps": [
{
"ImportPath": "github.com/coreos/go-iptables/iptables",
"Comment": "v0.1.0",
"Rev": "fbb73372b87f6e89951c2b6b31470c2c9d5cfae3"
},
{
"ImportPath": "github.com/coreos/go-systemd/activation",
"Comment": "v2-53-g2688e91",
"Rev": "2688e91251d9d8e404e86dd8f096e23b2f086958"
},
{
"ImportPath": "github.com/d2g/dhcp4",
"Rev": "f0e4d29ff0231dce36e250b2ed9ff08412584bca"
},
{
"ImportPath": "github.com/d2g/dhcp4client",
"Rev": "bed07e1bc5b85f69c6f0fd73393aa35ec68ed892"
},
{
"ImportPath": "github.com/onsi/ginkgo",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/config",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/codelocation",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/containernode",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/failer",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/leafnodes",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/remote",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/spec",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/specrunner",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/suite",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/testingtproxy",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/internal/writer",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/reporters",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/reporters/stenographer",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/ginkgo/types",
"Comment": "v1.2.0-29-g7f8ab55",
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
},
{
"ImportPath": "github.com/onsi/gomega",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/format",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/gbytes",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/gexec",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/internal/assertion",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/internal/asyncassertion",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/internal/oraclematcher",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/internal/testingtsupport",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/matchers",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/matchers/support/goraph/bipartitegraph",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/matchers/support/goraph/edge",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/matchers/support/goraph/node",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/matchers/support/goraph/util",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/onsi/gomega/types",
"Comment": "v1.0-71-g2152b45",
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
},
{
"ImportPath": "github.com/vishvananda/netlink",
"Rev": "ecf47fd5739b3d2c3daf7c89c4b9715a2605c21b"
},
{
"ImportPath": "github.com/vishvananda/netlink/nl",
"Rev": "ecf47fd5739b3d2c3daf7c89c4b9715a2605c21b"
},
{
"ImportPath": "golang.org/x/sys/unix",
"Rev": "e11762ca30adc5b39fdbfd8c4250dabeb8e456d3"
}
]
}

5
Godeps/Readme generated
View File

@ -1,5 +0,0 @@
This directory tree is generated automatically by godep.
Please do not edit.
See https://github.com/tools/godep for more information.

View File

@ -199,4 +199,3 @@
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.

View File

@ -1,5 +0,0 @@
Dan Williams <dcbw@redhat.com> (@dcbw)
Gabe Rosenhouse <grosenhouse@pivotal.io> (@rosenhouse)
Michael Bridgen <michael@weave.works> (@squaremo)
Stefan Junker <stefan.junker@coreos.com> (@steveeJ)
Tom Denham <tom.denham@metaswitch.com> (@tomdee)

10
OWNERS.md Normal file
View File

@ -0,0 +1,10 @@
# Owners
This is the official list of the CNI network plugins owners:
- Bruce Ma <brucema19901024@gmail.com> (@mars1024)
- Bryan Boreham <bryan@weave.works> (@bboreham)
- Casey Callendrello <cdc@redhat.com> (@squeed)
- Dan Williams <dcbw@redhat.com> (@dcbw)
- Gabe Rosenhouse <grosenhouse@pivotal.io> (@rosenhouse)
- Matt Dupre <matt@tigera.io> (@matthewdupre)
- Michael Cambria <mcambria@redhat.com> (@mccv1r0)
- Piotr Skarmuk <piotr.skarmuk@gmail.com> (@jellonek)

188
README.md
View File

@ -1,164 +1,34 @@
[![Build Status](https://travis-ci.org/containernetworking/cni.svg?branch=master)](https://travis-ci.org/containernetworking/cni)
[![Coverage Status](https://coveralls.io/repos/github/containernetworking/cni/badge.svg?branch=master)](https://coveralls.io/github/containernetworking/cni?branch=master)
[![Build Status](https://travis-ci.org/containernetworking/plugins.svg?branch=master)](https://travis-ci.org/containernetworking/plugins)
# CNI - the Container Network Interface
# plugins
Some CNI network plugins, maintained by the containernetworking team. For more information, see the individual READMEs.
## What is CNI?
Read [CONTRIBUTING](CONTRIBUTING.md) for build and test instructions.
The CNI (_Container Network Interface_) project consists of a specification and libraries for writing plugins to configure network interfaces in Linux containers, along with a number of supported plugins.
CNI concerns itself only with network connectivity of containers and removing allocated resources when the container is deleted.
Because of this focus CNI has a wide range of support and the specification is simple to implement.
## Plugins supplied:
### Main: interface-creating
* `bridge`: Creates a bridge, adds the host and the container to it.
* `ipvlan`: Adds an [ipvlan](https://www.kernel.org/doc/Documentation/networking/ipvlan.txt) interface in the container.
* `loopback`: Set the state of loopback interface to up.
* `macvlan`: Creates a new MAC address, forwards all traffic to that to the container.
* `ptp`: Creates a veth pair.
* `vlan`: Allocates a vlan device.
* `host-device`: Move an already-existing device into a container.
#### Windows: windows specific
* `win-bridge`: Creates a bridge, adds the host and the container to it.
* `win-overlay`: Creates an overlay interface to the container.
### IPAM: IP address allocation
* `dhcp`: Runs a daemon on the host to make DHCP requests on behalf of the container
* `host-local`: Maintains a local database of allocated IPs
* `static`: Allocate a static IPv4/IPv6 addresses to container and it's useful in debugging purpose.
As well as the [specification](SPEC.md), this repository contains the Go source code of a library for integrating CNI into applications, an example command-line tool, a template for making new plugins, and the supported plugins.
### Meta: other plugins
* `flannel`: Generates an interface corresponding to a flannel config file
* `tuning`: Tweaks sysctl parameters of an existing interface
* `portmap`: An iptables-based portmapping plugin. Maps ports from the host's address space to the container.
* `bandwidth`: Allows bandwidth-limiting through use of traffic control tbf (ingress/egress).
* `sbr`: A plugin that configures source based routing for an interface (from which it is chained).
* `firewall`: A firewall plugin which uses iptables or firewalld to add rules to allow traffic to/from the container.
The template code makes it straight-forward to create a CNI plugin for an existing container networking project.
CNI also makes a good framework for creating a new container networking project from scratch.
## Why develop CNI?
Application containers on Linux are a rapidly evolving area, and within this area networking is not well addressed as it is highly environment-specific.
We believe that many container runtimes and orchestrators will seek to solve the same problem of making the network layer pluggable.
To avoid duplication, we think it is prudent to define a common interface between the network plugins and container execution: hence we put forward this specification, along with libraries for Go and a set of plugins.
## Who is using CNI?
- [rkt - container engine](https://coreos.com/blog/rkt-cni-networking.html)
- [Kurma - container runtime](http://kurma.io/)
- [Kubernetes - a system to simplify container operations](http://kubernetes.io/docs/admin/network-plugins/)
- [Cloud Foundry - a platform for cloud applications](https://github.com/cloudfoundry-incubator/guardian-cni-adapter)
- [Weave - a multi-host Docker network](https://github.com/weaveworks/weave)
- [Project Calico - a layer 3 virtual network](https://github.com/projectcalico/calico-cni)
- [Contiv Networking - policy networking for various use cases](https://github.com/contiv/netplugin)
- [Mesos - a distributed systems kernel](https://github.com/apache/mesos/blob/master/docs/cni.md)
## Contributing to CNI
We welcome contributions, including [bug reports](https://github.com/containernetworking/cni/issues), and code and documentation improvements.
If you intend to contribute to code or documentation, please read [CONTRIBUTING.md](CONTRIBUTING.md). Also see the [contact section](#contact) in this README.
## How do I use CNI?
### Requirements
CNI requires Go 1.5+ to build.
Go 1.5 users will need to set GO15VENDOREXPERIMENT=1 to get vendored
dependencies. This flag is set by default in 1.6.
### Included Plugins
This repository includes a number of common plugins in the `plugins/` directory.
Please see the [Documentation/](Documentation/) directory for documentation about particular plugins.
### Running the plugins
The scripts/ directory contains two scripts, `priv-net-run.sh` and `docker-run.sh`, that can be used to exercise the plugins.
**note - priv-net-run.sh depends on `jq`**
Start out by creating a netconf file to describe a network:
```bash
$ mkdir -p /etc/cni/net.d
$ cat >/etc/cni/net.d/10-mynet.conf <<EOF
{
"name": "mynet",
"type": "bridge",
"bridge": "cni0",
"isGateway": true,
"ipMasq": true,
"ipam": {
"type": "host-local",
"subnet": "10.22.0.0/16",
"routes": [
{ "dst": "0.0.0.0/0" }
]
}
}
EOF
$ cat >/etc/cni/net.d/99-loopback.conf <<EOF
{
"type": "loopback"
}
EOF
```
The directory `/etc/cni/net.d` is the default location in which the scripts will look for net configurations.
Next, build the plugins:
```bash
$ ./build
```
Finally, execute a command (`ifconfig` in this example) in a private network namespace that has joined the `mynet` network:
```bash
$ CNI_PATH=`pwd`/bin
$ cd scripts
$ sudo CNI_PATH=$CNI_PATH ./priv-net-run.sh ifconfig
eth0 Link encap:Ethernet HWaddr f2:c2:6f:54:b8:2b
inet addr:10.22.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::f0c2:6fff:fe54:b82b/64 Scope:Link
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:1 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:1 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:90 (90.0 B) TX bytes:0 (0.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
```
The environment variable `CNI_PATH` tells the scripts and library where to look for plugin executables.
## Running a Docker container with network namespace set up by CNI plugins
Use the instructions in the previous section to define a netconf and build the plugins.
Next, docker-run.sh script wraps `docker run`, to execute the plugins prior to entering the container:
```bash
$ CNI_PATH=`pwd`/bin
$ cd scripts
$ sudo CNI_PATH=$CNI_PATH ./docker-run.sh --rm busybox:latest ifconfig
eth0 Link encap:Ethernet HWaddr fa:60:70:aa:07:d1
inet addr:10.22.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::f860:70ff:feaa:7d1/64 Scope:Link
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:1 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:1 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:90 (90.0 B) TX bytes:0 (0.0 B)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
```
## What might CNI do in the future?
CNI currently covers a wide range of needs for network configuration due to it simple model and API.
However, in the future CNI might want to branch out into other directions:
- Dynamic updates to existing network configuration
- Dynamic policies for network bandwidth and firewall rules
If these topics of are interest please contact the team via the mailing list or IRC and find some like minded people in the community to put a proposal together.
## Contact
For any questions about CNI, please reach out on the mailing list:
- Email: [cni-dev](https://groups.google.com/forum/#!forum/cni-dev)
- IRC: #[containernetworking](irc://irc.freenode.org:6667/#containernetworking) channel on freenode.org
### Sample
The sample plugin provides an example for building your own plugin.

35
RELEASING.md Normal file
View File

@ -0,0 +1,35 @@
# Release process
## Resulting artifacts
Creating a new release produces the following artifacts:
- Binaries (stored in the `release-<TAG>` directory) :
- `cni-plugins-<PLATFORM>-<VERSION>.tgz` binaries
- `cni-plugins-<VERSION>.tgz` binary (copy of amd64 platform binary)
- `sha1`, `sha256` and `sha512` files for the above files.
## Preparing for a release
1. Releases are performed by maintainers and should usually be discussed and planned at a maintainer meeting.
- Choose the version number. It should be prefixed with `v`, e.g. `v1.2.3`
- Take a quick scan through the PRs and issues to make sure there isn't anything crucial that _must_ be in the next release.
- Create a draft of the release note
- Discuss the level of testing that's needed and create a test plan if sensible
- Check what version of `go` is used in the build container, updating it if there's a new stable release.
- Update the vendor directory and Godeps to pin to the corresponding containernetworking/cni release. Create a PR, makes sure it passes CI and get it merged.
## Creating the release artifacts
1. Make sure you are on the master branch and don't have any local uncommitted changes.
1. Create a signed tag for the release `git tag -s $VERSION` (Ensure that GPG keys are created and added to GitHub)
1. Run the release script from the root of the repository
- `scripts/release.sh`
- The script requires Docker and ensures that a consistent environment is used.
- The artifacts will now be present in the `release-<TAG>` directory.
1. Test these binaries according to the test plan.
## Publishing the release
1. Push the tag to git `git push origin <TAG>`
1. Create a release on Github, using the tag which was just pushed.
1. Attach all the artifacts from the release directory.
1. Add the release note to the release.
1. Announce the release on at least the CNI mailing, IRC and Slack.

View File

@ -1,33 +0,0 @@
# CNI Roadmap
This document defines a high level roadmap for CNI development.
The list below is not complete, and we advise to get the current project state from the [milestones defined in GitHub](https://github.com/containernetworking/cni/milestones).
## CNI Milestones
### [v0.2.0](https://github.com/containernetworking/cni/milestones/v0.2.0)
* Signed release binaries
* Introduction of a testing strategy/framework
### [v0.3.0](https://github.com/containernetworking/cni/milestones/v0.3.0)
* Further increase test coverage
* Simpler default route handling in bridge plugin
* Clarify project description, documentation and contribution guidelines
### [v0.4.0](https://github.com/containernetworking/cni/milestones/v0.4.0)
* Further increase test coverage
* Simpler bridging of host interface
* Improve IPAM allocator predictability
* Allow in- and output of arbitrary K/V pairs for plugins
### [v1.0.0](https://github.com/containernetworking/cni/milestones/v1.0.0)
- Plugin composition functionality
- IPv6 support
- Stable SPEC
- Strategy and tooling for backwards compatibility
- Complete test coverage
- Integrate build artefact generation with CI

257
SPEC.md
View File

@ -1,257 +0,0 @@
# Container Networking Interface Proposal
## Overview
This document proposes a generic plugin-based networking solution for application containers on Linux, the _Container Networking Interface_, or _CNI_.
It is derived from the [rkt Networking Proposal][rkt-networking-proposal], which aimed to satisfy many of the [design considerations][rkt-networking-design] for networking in [rkt][rkt-github].
For the purposes of this proposal, we define two terms very specifically:
- _container_ can be considered synonymous with a [Linux _network namespace_][namespaces]. What unit this corresponds to depends on a particular container runtime implementation: for example, in implementations of the [App Container Spec][appc-github] like rkt, each _pod_ runs in a unique network namespace. In [Docker][docker], on the other hand, network namespaces generally exist for each separate Docker container.
- _network_ refers to a group of entities that are uniquely addressable that can communicate amongst each other. This could be either an individual container (as specified above), a machine, or some other network device (e.g. a router). Containers can be conceptually _added to_ or _removed from_ one or more networks.
[rkt-networking-proposal]: https://docs.google.com/a/coreos.com/document/d/1PUeV68q9muEmkHmRuW10HQ6cHgd4819_67pIxDRVNlM/edit#heading=h.ievko3xsjwxd
[rkt-networking-design]:
https://docs.google.com/a/coreos.com/document/d/1CTAL4gwqRofjxyp4tTkbgHtAwb2YCcP14UEbHNizd8g
[rkt-github]: https://github.com/coreos/rkt
[namespaces]: http://man7.org/linux/man-pages/man7/namespaces.7.html
[appc-github]: https://github.com/appc/spec
[docker]: https://docker.com
## General considerations
The intention is for the container runtime to first create a new network namespace for the container.
It then determines which networks this container should belong to and for each network, which plugin must be executed.
The network configuration is in JSON format and can easily be stored in a file.
The network configuration includes mandatory fields such as "name" and "type" as well as plugin (type) specific ones.
The container runtime sequentially sets up the networks by executing the corresponding plugin for each network.
Upon completion of the container lifecycle, the runtime executes the plugins in reverse order (relative to the order in which they were added) to disconnect them from the networks.
## CNI Plugin
### Overview
Each CNI plugin is implemented as an executable that is invoked by the container management system (e.g. rkt or Docker).
A CNI plugin is responsible for inserting a network interface into the container network namespace (e.g. one end of a veth pair) and making any necessary changes on the host (e.g. attaching other end of veth into a bridge).
It should then assign the IP to the interface and setup the routes consistent with IP Address Management section by invoking appropriate IPAM plugin.
### Parameters
The operations that the CNI plugin needs to support are:
- Add container to network
- Parameters:
- **Version**. The version of CNI spec that the caller is using (container management system or the invoking plugin).
- **Container ID**. This is optional but recommended, and should be unique across an administrative domain while the container is live (it may be reused in the future). For example, an environment with an IPAM system may require that each container is allocated a unique ID and that each IP allocation can thus be correlated back to a particular container. As another example, in appc implementations this would be the _pod ID_.
- **Network namespace path**. This represents the path to the network namespace to be added, i.e. /proc/[pid]/ns/net or a bind-mount/link to it.
- **Network configuration**. This is a JSON document describing a network to which a container can be joined. The schema is described below.
- **Extra arguments**. This allows granular configuration of CNI plugins on a per-container basis.
- **Name of the interface inside the container**. This is the name that should be assigned to the interface created inside the container (network namespace); consequently it must comply with the standard Linux restrictions on interface names.
- Result:
- **IPs assigned to the interface**. This is either an IPv4 address, an IPv6 address, or both.
- **DNS information**. Dictionary that includes DNS information for nameservers, domain, search domains and options.
- Delete container from network
- Parameters:
- **Version**. The version of CNI spec that the caller is using (container management system or the invoking plugin).
- **Container ID**, as defined above.
- **Network namespace path**, as defined above.
- **Network configuration**, as defined above.
- **Extra arguments**, as defined above.
- **Name of the interface inside the container**, as defined above.
The executable command-line API uses the type of network (see [Network Configuration](#network-configuration) below) as the name of the executable to invoke.
It will then look for this executable in a list of predefined directories. Once found, it will invoke the executable using the following environment variables for argument passing:
- `CNI_VERSION`: [Semantic Version 2.0](http://semver.org) of CNI specification. This effectively versions the CNI_XXX environment variables.
- `CNI_COMMAND`: indicates the desired operation; either `ADD` or `DEL`
- `CNI_CONTAINERID`: Container ID
- `CNI_NETNS`: Path to network namespace file
- `CNI_IFNAME`: Interface name to set up
- `CNI_ARGS`: Extra arguments passed in by the user at invocation time. Alphanumeric key-value pairs separated by semicolons; for example, "FOO=BAR;ABC=123"
- `CNI_PATH`: Colon-separated list of paths to search for CNI plugin executables
Network configuration in JSON format is streamed through stdin.
### Result
Success is indicated by a return code of zero and the following JSON printed to stdout in the case of the ADD command. This should be the same output as was returned by the IPAM plugin (see [IP Allocation](#ip-allocation) for details).
```
{
"cniVersion": "0.1.0",
"ip4": {
"ip": <ipv4-and-subnet-in-CIDR>,
"gateway": <ipv4-of-the-gateway>, (optional)
"routes": <list-of-ipv4-routes> (optional)
},
"ip6": {
"ip": <ipv6-and-subnet-in-CIDR>,
"gateway": <ipv6-of-the-gateway>, (optional)
"routes": <list-of-ipv6-routes> (optional)
},
"dns": {
"nameservers": <list-of-nameservers> (optional)
"domain": <name-of-local-domain> (optional)
"search": <list-of-additional-search-domains> (optional)
"options": <list-of-options> (optional)
}
}
```
`cniVersion` specifies a [Semantic Version 2.0](http://semver.org) of CNI specification used by the plugin.
`dns` field contains a dictionary consisting of common DNS information that this network is aware of.
The result is returned in the same format as specified in the [configuration](#network-configuration).
The specification does not declare how this information must be processed by CNI consumers.
Examples include generating an `/etc/resolv.conf` file to be injected into the container filesystem or running a DNS forwarder on the host.
Errors are indicated by a non-zero return code and the following JSON being printed to stdout:
```
{
"cniVersion": "0.1.0",
"code": <numeric-error-code>,
"msg": <short-error-message>,
"details": <long-error-message> (optional)
}
```
`cniVersion` specifies a [Semantic Version 2.0](http://semver.org) of CNI specification used by the plugin.
Error codes 0-99 are reserved for well-known errors (see [Well-known Error Codes](#well-known-error-codes) section).
Values of 100+ can be freely used for plugin specific errors.
In addition, stderr can be used for unstructured output such as logs.
### Network Configuration
The network configuration is described in JSON form. The configuration can be stored on disk or generated from other sources by the container runtime. The following fields are well-known and have the following meaning:
- `cniVersion` (string): [Semantic Version 2.0](http://semver.org) of CNI specification to which this configuration conforms.
- `name` (string): Network name. This should be unique across all containers on the host (or other administrative domain).
- `type` (string): Refers to the filename of the CNI plugin executable.
- `ipMasq` (boolean): Optional (if supported by the plugin). Set up an IP masquerade on the host for this network. This is necessary if the host will act as a gateway to subnets that are not able to route to the IP assigned to the container.
- `ipam`: Dictionary with IPAM specific values:
- `type` (string): Refers to the filename of the IPAM plugin executable.
- `routes` (list): List of subnets (in CIDR notation) that the CNI plugin should ensure are reachable by routing them through the network. Each entry is a dictionary containing:
- `dst` (string): subnet in CIDR notation
- `gw` (string): IP address of the gateway to use. If not specified, the default gateway for the subnet is assumed (as determined by the IPAM plugin).
- `dns`: Dictionary with DNS specific values:
- `nameservers` (list of strings): list of a priority-ordered list of DNS nameservers that this network is aware of. Each entry in the list is a string containing either an IPv4 or an IPv6 address.
- `domain` (string): the local domain used for short hostname lookups.
- `search` (list of strings): list of priority ordered search domains for short hostname lookups. Will be preferred over `domain` by most resolvers.
- `options` (list of strings): list of options that can be passed to the resolver
### Example configurations
```json
{
"cniVersion": "0.1.0",
"name": "dbnet",
"type": "bridge",
// type (plugin) specific
"bridge": "cni0",
"ipam": {
"type": "host-local",
// ipam specific
"subnet": "10.1.0.0/16",
"gateway": "10.1.0.1"
},
"dns": {
"nameservers": [ "10.1.0.1" ]
}
}
```
```json
{
"cniVersion": "0.1.0",
"name": "pci",
"type": "ovs",
// type (plugin) specific
"bridge": "ovs0",
"vxlanID": 42,
"ipam": {
"type": "dhcp",
"routes": [ { "dst": "10.3.0.0/16" }, { "dst": "10.4.0.0/16" } ]
}
}
```
```json
{
"cniVersion": "0.1",
"name": "wan",
"type": "macvlan",
// ipam specific
"ipam": {
"type": "dhcp",
"routes": [ { "dst": "10.0.0.0/8", "gw": "10.0.0.1" } ]
},
"dns": {
"nameservers": [ "10.0.0.1" ]
}
}
```
### IP Allocation
As part of its operation, a CNI plugin is expected to assign (and maintain) an IP address to the interface and install any necessary routes relevant for that interface. This gives the CNI plugin great flexibility but also places a large burden on it. Many CNI plugins would need to have the same code to support several IP management schemes that users may desire (e.g. dhcp, host-local).
To lessen the burden and make IP management strategy be orthogonal to the type of CNI plugin, we define a second type of plugin -- IP Address Management Plugin (IPAM plugin). It is however the responsibility of the CNI plugin to invoke the IPAM plugin at the proper moment in its execution. The IPAM plugin is expected to determine the interface IP/subnet, Gateway and Routes and return this information to the "main" plugin to apply. The IPAM plugin may obtain the information via a protocol (e.g. dhcp), data stored on a local filesystem, the "ipam" section of the Network Configuration file or a combination of the above.
#### IP Address Management (IPAM) Interface
Like CNI plugins, the IPAM plugins are invoked by running an executable. The executable is searched for in a predefined list of paths, indicated to the CNI plugin via `CNI_PATH`. The IPAM Plugin receives all the same environment variables that were passed in to the CNI plugin. Just like the CNI plugin, IPAM receives the network configuration file via stdin.
Success is indicated by a zero return code and the following JSON being printed to stdout (in the case of the ADD command):
```
{
"cniVersion": "0.1.0",
"ip4": {
"ip": <ipv4-and-subnet-in-CIDR>,
"gateway": <ipv4-of-the-gateway>, (optional)
"routes": <list-of-ipv4-routes> (optional)
},
"ip6": {
"ip": <ipv6-and-subnet-in-CIDR>,
"gateway": <ipv6-of-the-gateway>, (optional)
"routes": <list-of-ipv6-routes> (optional)
},
"dns": {
"nameservers": <list-of-nameservers> (optional)
"domain": <name-of-local-domain> (optional)
"search": <list-of-search-domains> (optional)
"options": <list-of-options> (optional)
}
}
```
`cniVersion` specifies a [Semantic Version 2.0](http://semver.org) of CNI specification used by the plugin.
`gateway` is the default gateway for this subnet, if one exists.
It does not instruct the CNI plugin to add any routes with this gateway: routes to add are specified separately via the `routes` field.
An example use of this value is for the CNI plugin to add this IP address to the linux-bridge to make it a gateway.
Each route entry is a dictionary with the following fields:
- `dst` (string): Destination subnet specified in CIDR notation.
- `gw` (string): IP of the gateway. If omitted, a default gateway is assumed (as determined by the CNI plugin).
The "dns" field contains a dictionary consisting of common DNS information.
- `nameservers` (list of strings): list of a priority-ordered list of DNS nameservers that this network is aware of. Each entry in the list is a string containing either an IPv4 or an IPv6 address.
- `domain` (string): the local domain used for short hostname lookups.
- `search` (list of strings): list of priority ordered search domains for short hostname lookups. Will be preferred over `domain` by most resolvers.
- `options` (list of strings): list of options that can be passed to the resolver
See [CNI Plugin Result](#result) section for more information.
Errors and logs are communicated in the same way as the CNI plugin. See [CNI Plugin Result](#result) section for details.
IPAM plugin examples:
- **host-local**: Select an unused (by other containers on the same host) IP within the specified range.
- **dhcp**: Use DHCP protocol to acquire and maintain a lease. The DHCP requests will be sent via the created container interface; therefore, the associated network must support broadcast.
#### Notes
- Routes are expected to be added with a 0 metric.
- A default route may be specified via "0.0.0.0/0". Since another network might have already configured the default route, the CNI plugin should be prepared to skip over its default route definition.
## Well-known Error Codes
- `1` - Incompatible CNI version

18
Vagrantfile vendored Normal file
View File

@ -0,0 +1,18 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure(2) do |config|
config.vm.box = "bento/ubuntu-16.04"
config.vm.synced_folder "..", "/go/src/github.com/containernetworking"
config.vm.provision "shell", inline: <<-SHELL
set -e -x -u
apt-get update -y || (sleep 40 && apt-get update -y)
apt-get install -y git gcc-multilib gcc-mingw-w64
wget -qO- https://storage.googleapis.com/golang/go1.12.7.linux-amd64.tar.gz | tar -C /usr/local -xz
echo 'export GOPATH=/go' >> /root/.bashrc
echo 'export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin' >> /root/.bashrc
cd /go/src/github.com/containernetworking/plugins
SHELL
end

30
build
View File

@ -1,30 +0,0 @@
#!/usr/bin/env bash
set -e
ORG_PATH="github.com/containernetworking"
REPO_PATH="${ORG_PATH}/cni"
if [ ! -h gopath/src/${REPO_PATH} ]; then
mkdir -p gopath/src/${ORG_PATH}
ln -s ../../../.. gopath/src/${REPO_PATH} || exit 255
fi
export GO15VENDOREXPERIMENT=1
export GOBIN=${PWD}/bin
export GOPATH=${PWD}/gopath
echo "Building API"
go build "$@" ${REPO_PATH}/libcni
echo "Building reference CLI"
go install "$@" ${REPO_PATH}/cnitool
echo "Building plugins"
PLUGINS="plugins/meta/* plugins/main/* plugins/ipam/*"
for d in $PLUGINS; do
if [ -d $d ]; then
plugin=$(basename $d)
echo " " $plugin
go install "$@" ${REPO_PATH}/$d
fi
done

33
build_linux.sh Executable file
View File

@ -0,0 +1,33 @@
#!/usr/bin/env bash
set -e
cd $(dirname "$0")
if [ "$(uname)" == "Darwin" ]; then
export GOOS="${GOOS:-linux}"
fi
ORG_PATH="github.com/containernetworking"
export REPO_PATH="${ORG_PATH}/plugins"
if [ ! -h gopath/src/${REPO_PATH} ]; then
mkdir -p gopath/src/${ORG_PATH}
ln -s ../../../.. gopath/src/${REPO_PATH} || exit 255
fi
export GOPATH=${PWD}/gopath
export GO="${GO:-go}"
export GOFLAGS="${GOFLAGS} -mod=vendor"
mkdir -p "${PWD}/bin"
echo "Building plugins ${GOOS}"
PLUGINS="plugins/meta/* plugins/main/* plugins/ipam/*"
for d in $PLUGINS; do
if [ -d "$d" ]; then
plugin="$(basename "$d")"
if [ $plugin != "windows" ]; then
echo " $plugin"
$GO build -o "${PWD}/bin/$plugin" "$@" "$REPO_PATH"/$d
fi
fi
done

25
build_windows.sh Executable file
View File

@ -0,0 +1,25 @@
#!/usr/bin/env bash
set -e
cd $(dirname "$0")
ORG_PATH="github.com/containernetworking"
export REPO_PATH="${ORG_PATH}/plugins"
export GOPATH=$(mktemp -d)
mkdir -p ${GOPATH}/src/${ORG_PATH}
trap "{ rm -rf $GOPATH; }" EXIT
ln -s ${PWD} ${GOPATH}/src/${REPO_PATH} || exit 255
export GO="${GO:-go}"
export GOOS=windows
export GOFLAGS="${GOFLAGS} -mod=vendor"
echo $GOFLAGS
PLUGINS=$(cat plugins/windows_only.txt)
for d in $PLUGINS; do
if [ -d "$d" ]; then
plugin="$(basename "$d").exe"
echo " $plugin"
$GO build -o "${PWD}/bin/$plugin" "$@" "$REPO_PATH"/$d
fi
done

View File

@ -1,87 +0,0 @@
// Copyright 2015 CoreOS, Inc.
//
// 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.
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/containernetworking/cni/libcni"
)
const (
EnvCNIPath = "CNI_PATH"
EnvNetDir = "NETCONFPATH"
DefaultNetDir = "/etc/cni/net.d"
CmdAdd = "add"
CmdDel = "del"
)
func main() {
if len(os.Args) < 3 {
usage()
return
}
netdir := os.Getenv(EnvNetDir)
if netdir == "" {
netdir = DefaultNetDir
}
netconf, err := libcni.LoadConf(netdir, os.Args[2])
if err != nil {
exit(err)
}
netns := os.Args[3]
cninet := &libcni.CNIConfig{
Path: strings.Split(os.Getenv(EnvCNIPath), ":"),
}
rt := &libcni.RuntimeConf{
ContainerID: "cni",
NetNS: netns,
IfName: "eth0",
}
switch os.Args[1] {
case CmdAdd:
_, err := cninet.AddNetwork(netconf, rt)
exit(err)
case CmdDel:
exit(cninet.DelNetwork(netconf, rt))
}
}
func usage() {
exe := filepath.Base(os.Args[0])
fmt.Fprintf(os.Stderr, "%s: Add or remove network interfaces from a network namespace\n", exe)
fmt.Fprintf(os.Stderr, " %s %s <net> <netns>\n", exe, CmdAdd)
fmt.Fprintf(os.Stderr, " %s %s <net> <netns>\n", exe, CmdDel)
os.Exit(1)
}
func exit(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err)
os.Exit(1)
}
os.Exit(0)
}

33
go.mod Normal file
View File

@ -0,0 +1,33 @@
module github.com/containernetworking/plugins
go 1.12
require (
github.com/Microsoft/go-winio v0.4.11 // indirect
github.com/Microsoft/hcsshim v0.8.6
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44
github.com/containernetworking/cni v0.7.1
github.com/coreos/go-iptables v0.4.5
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c
github.com/d2g/dhcp4client v1.0.0
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4 // indirect
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c
github.com/golang/protobuf v1.3.1 // indirect
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56
github.com/mattn/go-shellwords v1.0.3
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8
github.com/sirupsen/logrus v1.0.6 // indirect
github.com/stretchr/testify v1.3.0 // indirect
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc // indirect
golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941 // indirect
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1 // indirect
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f
gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect
)

59
go.sum Normal file
View File

@ -0,0 +1,59 @@
github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0IA=
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae h1:AMzIhMUqU3jMrZiTuW0zkYeKlKDAFD+DG20IoO421/Y=
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 h1:y853v6rXx+zefEcjET3JuKAqvhj+FKflQijjeaSv2iA=
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/containernetworking/cni v0.7.1 h1:fE3r16wpSEyaqY4Z4oFrLMmIGfBYIKpPrHK31EJ9FzE=
github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
github.com/coreos/go-iptables v0.4.5 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38=
github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c h1:Xo2rK1pzOm0jO6abTPIQwbAmqBIOj132otexc1mmzFc=
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
github.com/d2g/dhcp4client v1.0.0 h1:suYBsYZIkSlUMEz4TAYCczKf62IA2UWC+O8+KtdOhCo=
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5 h1:+CpLbZIeUn94m02LdEKPcgErLJ347NUwxPKs5u8ieiY=
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4 h1:itqmmf1PFpC4n5JW+j4BU7X4MTfVurhYRTjODoPb2Y8=
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c h1:RBUpb2b14UnmRHNd2uHz20ZHLDK+SW5Us/vWF5IHRaY=
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56 h1:742eGXur0715JMq73aD95/FU0XpVKXqNuTnEfXsLOYQ=
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
github.com/mattn/go-shellwords v1.0.3 h1:K/VxK7SZ+cvuPgFSLKi5QPI9Vr/ipOf4C1gN+ntueUk=
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b h1:Ey6yH0acn50T/v6CB75bGP4EMJqnv9WvnjN7oZaj+xE=
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a h1:KfNOeFvoAssuZLT7IntKZElKwi/5LRuxY71k+t6rfaM=
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 h1:2c1EFnZHIPCW8qKWgHMH/fX2PkSabFc5mrVzfUNdg5U=
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
github.com/sirupsen/logrus v1.0.6 h1:hcP1GmhGigz/O7h1WVUM5KklBp1JoNS9FggWKdj/j3s=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf h1:3J37+NPjNyGW/dbfXtj3yWuF9OEepIdGOXRaJGbORV8=
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc h1:R83G5ikgLMxrBvLh22JhdfI8K6YXEPHx5P03Uu3DRs4=
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941 h1:qBTHLajHecfu+xzRI9PqVDcqx7SdHj9d4B+EzSn3tAc=
golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1 h1:Y/KGZSOdz/2r0WJ9Mkmz6NJBusp0kiNx1Cn82lzJQ6w=
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0=
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=

View File

@ -0,0 +1,270 @@
// Copyright 2018 CNI authors
//
// 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.
package integration_test
import (
"fmt"
"math/rand"
"os"
"os/exec"
"path/filepath"
"bytes"
"io"
"net"
"regexp"
"strconv"
"strings"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gbytes"
"github.com/onsi/gomega/gexec"
)
var _ = Describe("Basic PTP using cnitool", func() {
var (
cnitoolBin string
cniPath string
)
BeforeEach(func() {
var err error
cniPath, err = filepath.Abs("../bin")
Expect(err).NotTo(HaveOccurred())
cnitoolBin, err = exec.LookPath("cnitool")
Expect(err).NotTo(HaveOccurred(), "expected to find cnitool in your PATH")
})
Context("basic cases", func() {
var (
env TestEnv
hostNS Namespace
contNS Namespace
)
BeforeEach(func() {
var err error
netConfPath, err := filepath.Abs("./testdata")
Expect(err).NotTo(HaveOccurred())
env = TestEnv([]string{
"CNI_PATH=" + cniPath,
"NETCONFPATH=" + netConfPath,
"PATH=" + os.Getenv("PATH"),
})
hostNS = Namespace(fmt.Sprintf("cni-test-host-%x", rand.Int31()))
hostNS.Add()
contNS = Namespace(fmt.Sprintf("cni-test-cont-%x", rand.Int31()))
contNS.Add()
})
AfterEach(func() {
contNS.Del()
hostNS.Del()
})
basicAssertion := func(netName, expectedIPPrefix string) {
env.runInNS(hostNS, cnitoolBin, "add", netName, contNS.LongName())
addrOutput := env.runInNS(contNS, "ip", "addr")
Expect(addrOutput).To(ContainSubstring(expectedIPPrefix))
env.runInNS(hostNS, cnitoolBin, "del", netName, contNS.LongName())
}
It("supports basic network add and del operations", func() {
basicAssertion("basic-ptp", "10.1.2.")
})
It("supports add and del with ptp + bandwidth", func() {
basicAssertion("chained-ptp-bandwidth", "10.9.2.")
})
})
Context("when the bandwidth plugin is chained with a plugin that returns multiple adapters", func() {
var (
hostNS Namespace
contNS1 Namespace
contNS2 Namespace
basicBridgeEnv TestEnv
chainedBridgeBandwidthEnv TestEnv
chainedBridgeBandwidthSession, basicBridgeSession *gexec.Session
)
BeforeEach(func() {
hostNS = Namespace(fmt.Sprintf("cni-test-host-%x", rand.Int31()))
hostNS.Add()
contNS1 = Namespace(fmt.Sprintf("cni-test-cont1-%x", rand.Int31()))
contNS1.Add()
contNS2 = Namespace(fmt.Sprintf("cni-test-cont2-%x", rand.Int31()))
contNS2.Add()
basicBridgeNetConfPath, err := filepath.Abs("./testdata/basic-bridge")
Expect(err).NotTo(HaveOccurred())
basicBridgeEnv = TestEnv([]string{
"CNI_PATH=" + cniPath,
"NETCONFPATH=" + basicBridgeNetConfPath,
"PATH=" + os.Getenv("PATH"),
})
chainedBridgeBandwidthNetConfPath, err := filepath.Abs("./testdata/chained-bridge-bandwidth")
Expect(err).NotTo(HaveOccurred())
chainedBridgeBandwidthEnv = TestEnv([]string{
"CNI_PATH=" + cniPath,
"NETCONFPATH=" + chainedBridgeBandwidthNetConfPath,
"PATH=" + os.Getenv("PATH"),
})
})
AfterEach(func() {
if chainedBridgeBandwidthSession != nil {
chainedBridgeBandwidthSession.Kill()
}
if basicBridgeSession != nil {
basicBridgeSession.Kill()
}
chainedBridgeBandwidthEnv.runInNS(hostNS, cnitoolBin, "del", "network-chain-test", contNS1.LongName())
basicBridgeEnv.runInNS(hostNS, cnitoolBin, "del", "network-chain-test", contNS2.LongName())
})
Measure("limits traffic only on the restricted bandwith veth device", func(b Benchmarker) {
ipRegexp := regexp.MustCompile("10\\.1[12]\\.2\\.\\d{1,3}")
By(fmt.Sprintf("adding %s to %s\n\n", "chained-bridge-bandwidth", contNS1.ShortName()))
chainedBridgeBandwidthEnv.runInNS(hostNS, cnitoolBin, "add", "network-chain-test", contNS1.LongName())
chainedBridgeIP := ipRegexp.FindString(chainedBridgeBandwidthEnv.runInNS(contNS1, "ip", "addr"))
Expect(chainedBridgeIP).To(ContainSubstring("10.12.2."))
By(fmt.Sprintf("adding %s to %s\n\n", "basic-bridge", contNS2.ShortName()))
basicBridgeEnv.runInNS(hostNS, cnitoolBin, "add", "network-chain-test", contNS2.LongName())
basicBridgeIP := ipRegexp.FindString(basicBridgeEnv.runInNS(contNS2, "ip", "addr"))
Expect(basicBridgeIP).To(ContainSubstring("10.11.2."))
var chainedBridgeBandwidthPort, basicBridgePort int
var err error
By(fmt.Sprintf("starting echo server in %s\n\n", contNS1.ShortName()))
chainedBridgeBandwidthPort, chainedBridgeBandwidthSession, err = startEchoServerInNamespace(contNS1)
Expect(err).ToNot(HaveOccurred())
By(fmt.Sprintf("starting echo server in %s\n\n", contNS2.ShortName()))
basicBridgePort, basicBridgeSession, err = startEchoServerInNamespace(contNS2)
Expect(err).ToNot(HaveOccurred())
packetInBytes := 20000 // The shaper needs to 'warm'. Send enough to cause it to throttle,
// balanced by run time.
By(fmt.Sprintf("sending tcp traffic to the chained, bridged, traffic shaped container on ip address '%s:%d'\n\n", chainedBridgeIP, chainedBridgeBandwidthPort))
runtimeWithLimit := b.Time("with chained bridge and bandwidth plugins", func() {
makeTcpClientInNS(hostNS.ShortName(), chainedBridgeIP, chainedBridgeBandwidthPort, packetInBytes)
})
By(fmt.Sprintf("sending tcp traffic to the basic bridged container on ip address '%s:%d'\n\n", basicBridgeIP, basicBridgePort))
runtimeWithoutLimit := b.Time("with basic bridged plugin", func() {
makeTcpClientInNS(hostNS.ShortName(), basicBridgeIP, basicBridgePort, packetInBytes)
})
Expect(runtimeWithLimit).To(BeNumerically(">", runtimeWithoutLimit+1000*time.Millisecond))
}, 1)
})
})
type TestEnv []string
func (e TestEnv) run(bin string, args ...string) string {
cmd := exec.Command(bin, args...)
cmd.Env = e
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
Expect(err).NotTo(HaveOccurred())
Eventually(session, "5s").Should(gexec.Exit(0))
return string(session.Out.Contents())
}
func (e TestEnv) runInNS(nsShortName Namespace, bin string, args ...string) string {
a := append([]string{"netns", "exec", string(nsShortName), bin}, args...)
return e.run("ip", a...)
}
type Namespace string
func (n Namespace) LongName() string {
return fmt.Sprintf("/var/run/netns/%s", n)
}
func (n Namespace) ShortName() string {
return string(n)
}
func (n Namespace) Add() {
(TestEnv{}).run("ip", "netns", "add", string(n))
}
func (n Namespace) Del() {
(TestEnv{}).run("ip", "netns", "del", string(n))
}
func makeTcpClientInNS(netns string, address string, port int, numBytes int) {
message := bytes.Repeat([]byte{'a'}, numBytes)
bin, err := exec.LookPath("nc")
Expect(err).NotTo(HaveOccurred())
var cmd *exec.Cmd
if netns != "" {
netns = filepath.Base(netns)
cmd = exec.Command("ip", "netns", "exec", netns, bin, "-v", address, strconv.Itoa(port))
} else {
cmd = exec.Command("nc", address, strconv.Itoa(port))
}
cmd.Stdin = bytes.NewBuffer([]byte(message))
cmd.Stderr = GinkgoWriter
out, err := cmd.Output()
Expect(err).NotTo(HaveOccurred())
Expect(string(out)).To(Equal(string(message)))
}
func startEchoServerInNamespace(netNS Namespace) (int, *gexec.Session, error) {
session, err := startInNetNS(echoServerBinaryPath, netNS)
Expect(err).NotTo(HaveOccurred())
// wait for it to print it's address on stdout
Eventually(session.Out).Should(gbytes.Say("\n"))
_, portString, err := net.SplitHostPort(strings.TrimSpace(string(session.Out.Contents())))
Expect(err).NotTo(HaveOccurred())
port, err := strconv.Atoi(portString)
Expect(err).NotTo(HaveOccurred())
go func() {
// print out echoserver output to ginkgo to capture any errors that might be occurring.
io.Copy(GinkgoWriter, io.MultiReader(session.Out, session.Err))
}()
return port, session, nil
}
func startInNetNS(binPath string, namespace Namespace) (*gexec.Session, error) {
cmd := exec.Command("ip", "netns", "exec", namespace.ShortName(), binPath)
return gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
}

View File

@ -0,0 +1,44 @@
// Copyright 2018 CNI authors
//
// 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.
package integration_test
import (
"math/rand"
"testing"
. "github.com/onsi/ginkgo"
"github.com/onsi/ginkgo/config"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
)
func TestIntegration(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "integration")
}
var echoServerBinaryPath string
var _ = SynchronizedBeforeSuite(func() []byte {
binaryPath, err := gexec.Build("github.com/containernetworking/plugins/pkg/testutils/echosvr")
Expect(err).NotTo(HaveOccurred())
return []byte(binaryPath)
}, func(data []byte) {
echoServerBinaryPath = string(data)
rand.Seed(config.GinkgoConfig.RandomSeed + int64(GinkgoParallelNode()))
})
var _ = SynchronizedAfterSuite(func() {}, func() {
gexec.CleanupBuildArtifacts()
})

View File

@ -0,0 +1,12 @@
{
"cniVersion": "0.3.1",
"name": "network-chain-test",
"type": "bridge",
"bridge": "test-bridge-0",
"isDefaultGateway": true,
"ipam": {
"type": "host-local",
"subnet": "10.11.2.0/24",
"dataDir": "/tmp/foo"
}
}

11
integration/testdata/basic-ptp.json vendored Normal file
View File

@ -0,0 +1,11 @@
{
"cniVersion": "0.3.0",
"name": "basic-ptp",
"type": "ptp",
"ipMasq": true,
"mtu": 512,
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24"
}
}

View File

@ -0,0 +1,27 @@
{
"cniVersion": "0.3.1",
"name": "network-chain-test",
"plugins": [
{
"type": "bridge",
"bridge": "test-bridge-1",
"isDefaultGateway": true,
"ipam": {
"type": "host-local",
"subnet": "10.12.2.0/24",
"dataDir": "/tmp/bar"
}
},
{
"type": "bandwidth",
"runtimeConfig": {
"bandWidth": {
"ingressRate": 8000,
"ingressBurst": 16000,
"egressRate": 8000,
"egressBurst": 16000
}
}
}
]
}

View File

@ -0,0 +1,26 @@
{
"cniVersion": "0.3.1",
"name": "chained-ptp-bandwidth",
"plugins": [
{
"type": "ptp",
"ipMasq": true,
"mtu": 512,
"ipam": {
"type": "host-local",
"subnet": "10.9.2.0/24"
}
},
{
"type": "bandwidth",
"runtimeConfig": {
"bandWidth": {
"ingressRate": 800,
"ingressBurst": 200,
"egressRate": 800,
"egressBurst": 200
}
}
}
]
}

View File

@ -1,73 +0,0 @@
// Copyright 2015 CNI authors
//
// 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.
package libcni
import (
"strings"
"github.com/containernetworking/cni/pkg/invoke"
"github.com/containernetworking/cni/pkg/types"
)
type RuntimeConf struct {
ContainerID string
NetNS string
IfName string
Args [][2]string
}
type NetworkConfig struct {
Network *types.NetConf
Bytes []byte
}
type CNI interface {
AddNetwork(net *NetworkConfig, rt *RuntimeConf) (*types.Result, error)
DelNetwork(net *NetworkConfig, rt *RuntimeConf) error
}
type CNIConfig struct {
Path []string
}
func (c *CNIConfig) AddNetwork(net *NetworkConfig, rt *RuntimeConf) (*types.Result, error) {
pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
if err != nil {
return nil, err
}
return invoke.ExecPluginWithResult(pluginPath, net.Bytes, c.args("ADD", rt))
}
func (c *CNIConfig) DelNetwork(net *NetworkConfig, rt *RuntimeConf) error {
pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path)
if err != nil {
return err
}
return invoke.ExecPluginWithoutResult(pluginPath, net.Bytes, c.args("DEL", rt))
}
// =====
func (c *CNIConfig) args(action string, rt *RuntimeConf) *invoke.Args {
return &invoke.Args{
Command: action,
ContainerID: rt.ContainerID,
NetNS: rt.NetNS,
PluginArgs: rt.Args,
IfName: rt.IfName,
Path: strings.Join(c.Path, ":"),
}
}

View File

@ -1,85 +0,0 @@
// Copyright 2015 CNI authors
//
// 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.
package libcni
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
)
func ConfFromBytes(bytes []byte) (*NetworkConfig, error) {
conf := &NetworkConfig{Bytes: bytes}
if err := json.Unmarshal(bytes, &conf.Network); err != nil {
return nil, fmt.Errorf("error parsing configuration: %s", err)
}
return conf, nil
}
func ConfFromFile(filename string) (*NetworkConfig, error) {
bytes, err := ioutil.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("error reading %s: %s", filename, err)
}
return ConfFromBytes(bytes)
}
func ConfFiles(dir string) ([]string, error) {
// In part, adapted from rkt/networking/podenv.go#listFiles
files, err := ioutil.ReadDir(dir)
switch {
case err == nil: // break
case os.IsNotExist(err):
return nil, nil
default:
return nil, err
}
confFiles := []string{}
for _, f := range files {
if f.IsDir() {
continue
}
if filepath.Ext(f.Name()) == ".conf" {
confFiles = append(confFiles, filepath.Join(dir, f.Name()))
}
}
return confFiles, nil
}
func LoadConf(dir, name string) (*NetworkConfig, error) {
files, err := ConfFiles(dir)
switch {
case err != nil:
return nil, err
case len(files) == 0:
return nil, fmt.Errorf("no net configurations found")
}
sort.Strings(files)
for _, confFile := range files {
conf, err := ConfFromFile(confFile)
if err != nil {
return nil, err
}
if conf.Network.Name == name {
return conf, nil
}
}
return nil, fmt.Errorf(`no net configuration with name "%s" in %s`, name, dir)
}

37
pkg/errors/errors.go Normal file
View File

@ -0,0 +1,37 @@
// Copyright 2020 CNI authors
//
// 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.
package errors
import "fmt"
// Annotate is used to add extra context to an existing error. The return will be
// a new error which carries error message from both context message and existing error.
func Annotate(err error, message string) error {
if err == nil {
return nil
}
return fmt.Errorf("%s: %v", message, err)
}
// Annotatef is used to add extra context with args to an existing error. The return will be
// a new error which carries error message from both context message and existing error.
func Annotatef(err error, message string, args ...interface{}) error {
if err == nil {
return nil
}
return fmt.Errorf("%s: %v", fmt.Sprintf(message, args...), err)
}

96
pkg/errors/errors_test.go Normal file
View File

@ -0,0 +1,96 @@
// Copyright 2020 CNI authors
//
// 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.
package errors
import (
"errors"
"reflect"
"testing"
)
func TestAnnotate(t *testing.T) {
tests := []struct {
name string
existingErr error
contextMessage string
expectedErr error
}{
{
"nil error",
nil,
"context",
nil,
},
{
"normal case",
errors.New("existing error"),
"context",
errors.New("context: existing error"),
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if !reflect.DeepEqual(Annotatef(test.existingErr, test.contextMessage), test.expectedErr) {
t.Errorf("test case %s fails", test.name)
return
}
})
}
}
func TestAnnotatef(t *testing.T) {
tests := []struct {
name string
existingErr error
contextMessage string
contextArgs []interface{}
expectedErr error
}{
{
"nil error",
nil,
"context",
nil,
nil,
},
{
"normal case",
errors.New("existing error"),
"context",
nil,
errors.New("context: existing error"),
},
{
"normal case with args",
errors.New("existing error"),
"context %s %d",
[]interface{}{
"arg",
100,
},
errors.New("context arg 100: existing error"),
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if !reflect.DeepEqual(Annotatef(test.existingErr, test.contextMessage, test.contextArgs...), test.expectedErr) {
t.Errorf("test case %s fails", test.name)
return
}
})
}
}

370
pkg/hns/endpoint_windows.go Normal file
View File

@ -0,0 +1,370 @@
// Copyright 2017 CNI authors
//
// 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.
package hns
import (
"fmt"
"net"
"strings"
"github.com/Microsoft/hcsshim"
"github.com/Microsoft/hcsshim/hcn"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/plugins/pkg/errors"
)
const (
pauseContainerNetNS = "none"
)
type EndpointInfo struct {
EndpointName string
DNS types.DNS
NetworkName string
NetworkId string
Gateway net.IP
IpAddress net.IP
}
// GetSandboxContainerID returns the sandbox ID of this pod
func GetSandboxContainerID(containerID string, netNs string) string {
if len(netNs) != 0 && netNs != pauseContainerNetNS {
splits := strings.SplitN(netNs, ":", 2)
if len(splits) == 2 {
containerID = splits[1]
}
}
return containerID
}
// short function so we know when to return "" for a string
func GetIpString(ip *net.IP) string {
if len(*ip) == 0 {
return ""
} else {
return ip.String()
}
}
func GenerateHnsEndpoint(epInfo *EndpointInfo, n *NetConf) (*hcsshim.HNSEndpoint, error) {
// run the IPAM plugin and get back the config to apply
hnsEndpoint, err := hcsshim.GetHNSEndpointByName(epInfo.EndpointName)
if err != nil && !hcsshim.IsNotExist(err) {
return nil, errors.Annotatef(err, "failed to get endpoint %q", epInfo.EndpointName)
}
if hnsEndpoint != nil {
if hnsEndpoint.VirtualNetwork != epInfo.NetworkId {
_, err = hnsEndpoint.Delete()
if err != nil {
return nil, errors.Annotatef(err, "failed to delete endpoint %s", epInfo.EndpointName)
}
hnsEndpoint = nil
}
}
if n.LoopbackDSR {
n.ApplyLoopbackDSR(&epInfo.IpAddress)
}
if hnsEndpoint == nil {
hnsEndpoint = &hcsshim.HNSEndpoint{
Name: epInfo.EndpointName,
VirtualNetwork: epInfo.NetworkId,
DNSServerList: strings.Join(epInfo.DNS.Nameservers, ","),
DNSSuffix: strings.Join(epInfo.DNS.Search, ","),
GatewayAddress: GetIpString(&epInfo.Gateway),
IPAddress: epInfo.IpAddress,
Policies: n.MarshalPolicies(),
}
}
return hnsEndpoint, nil
}
func GenerateHcnEndpoint(epInfo *EndpointInfo, n *NetConf) (*hcn.HostComputeEndpoint, error) {
// run the IPAM plugin and get back the config to apply
hcnEndpoint, err := hcn.GetEndpointByName(epInfo.EndpointName)
if err != nil && !hcn.IsNotFoundError(err) {
return nil, errors.Annotatef(err, "failed to get endpoint %q", epInfo.EndpointName)
}
if hcnEndpoint != nil {
// If the endpont already exists, then we should return error unless
// the endpoint is based on a different network then delete
// should that fail return error
if !strings.EqualFold(hcnEndpoint.HostComputeNetwork, epInfo.NetworkId) {
err = hcnEndpoint.Delete()
if err != nil {
return nil, errors.Annotatef(err, "failed to delete endpoint %s", epInfo.EndpointName)
}
} else {
return nil, fmt.Errorf("endpoint %q already exits", epInfo.EndpointName)
}
}
if hcnEndpoint == nil {
routes := []hcn.Route{
{
NextHop: GetIpString(&epInfo.Gateway),
DestinationPrefix: GetDefaultDestinationPrefix(&epInfo.Gateway),
},
}
hcnDns := hcn.Dns{
Search: epInfo.DNS.Search,
ServerList: epInfo.DNS.Nameservers,
}
hcnIpConfig := hcn.IpConfig{
IpAddress: GetIpString(&epInfo.IpAddress),
}
ipConfigs := []hcn.IpConfig{hcnIpConfig}
if n.LoopbackDSR {
n.ApplyLoopbackDSR(&epInfo.IpAddress)
}
hcnEndpoint = &hcn.HostComputeEndpoint{
SchemaVersion: hcn.Version{Major: 2},
Name: epInfo.EndpointName,
HostComputeNetwork: epInfo.NetworkId,
Dns: hcnDns,
Routes: routes,
IpConfigurations: ipConfigs,
Policies: func() []hcn.EndpointPolicy {
if n.HcnPolicyArgs == nil {
n.HcnPolicyArgs = []hcn.EndpointPolicy{}
}
return n.HcnPolicyArgs
}(),
}
}
return hcnEndpoint, nil
}
// ConstructEndpointName constructs enpointId which is used to identify an endpoint from HNS
// There is a special consideration for netNs name here, which is required for Windows Server 1709
// containerID is the Id of the container on which the endpoint is worked on
func ConstructEndpointName(containerID string, netNs string, networkName string) string {
return GetSandboxContainerID(containerID, netNs) + "_" + networkName
}
// DeprovisionEndpoint removes an endpoint from the container by sending a Detach request to HNS
// For shared endpoint, ContainerDetach is used
// for removing the endpoint completely, HotDetachEndpoint is used
func DeprovisionEndpoint(epName string, netns string, containerID string) error {
if len(netns) == 0 {
return nil
}
hnsEndpoint, err := hcsshim.GetHNSEndpointByName(epName)
if hcsshim.IsNotExist(err) {
return nil
} else if err != nil {
return errors.Annotatef(err, "failed to find HNSEndpoint %s", epName)
}
if netns != pauseContainerNetNS {
// Shared endpoint removal. Do not remove the endpoint.
hnsEndpoint.ContainerDetach(containerID)
return nil
}
// Do not consider this as failure, else this would leak endpoints
hcsshim.HotDetachEndpoint(containerID, hnsEndpoint.Id)
// Do not return error
hnsEndpoint.Delete()
return nil
}
type EndpointMakerFunc func() (*hcsshim.HNSEndpoint, error)
// ProvisionEndpoint provisions an endpoint to a container specified by containerID.
// If an endpoint already exists, the endpoint is reused.
// This call is idempotent
func ProvisionEndpoint(epName string, expectedNetworkId string, containerID string, netns string, makeEndpoint EndpointMakerFunc) (*hcsshim.HNSEndpoint, error) {
// On the second add call we expect that the endpoint already exists. If it
// does not then we should return an error.
if netns != pauseContainerNetNS {
_, err := hcsshim.GetHNSEndpointByName(epName)
if err != nil {
return nil, errors.Annotatef(err, "failed to find HNSEndpoint %s", epName)
}
}
// check if endpoint already exists
createEndpoint := true
hnsEndpoint, err := hcsshim.GetHNSEndpointByName(epName)
if hnsEndpoint != nil && strings.EqualFold(hnsEndpoint.VirtualNetwork, expectedNetworkId) {
createEndpoint = false
}
if createEndpoint {
if hnsEndpoint != nil {
if _, err = hnsEndpoint.Delete(); err != nil {
return nil, errors.Annotate(err, "failed to delete the stale HNSEndpoint")
}
}
if hnsEndpoint, err = makeEndpoint(); err != nil {
return nil, errors.Annotate(err, "failed to make a new HNSEndpoint")
}
if hnsEndpoint, err = hnsEndpoint.Create(); err != nil {
return nil, errors.Annotate(err, "failed to create the new HNSEndpoint")
}
}
// hot attach
if err := hcsshim.HotAttachEndpoint(containerID, hnsEndpoint.Id); err != nil {
if createEndpoint {
err := DeprovisionEndpoint(epName, netns, containerID)
if err != nil {
return nil, errors.Annotatef(err, "failed to Deprovsion after HotAttach failure")
}
}
if hcsshim.ErrComputeSystemDoesNotExist == err {
return hnsEndpoint, nil
}
return nil, err
}
return hnsEndpoint, nil
}
type HcnEndpointMakerFunc func() (*hcn.HostComputeEndpoint, error)
func AddHcnEndpoint(epName string, expectedNetworkId string, namespace string,
makeEndpoint HcnEndpointMakerFunc) (*hcn.HostComputeEndpoint, error) {
hcnEndpoint, err := makeEndpoint()
if err != nil {
return nil, errors.Annotate(err, "failed to make a new HNSEndpoint")
}
if hcnEndpoint, err = hcnEndpoint.Create(); err != nil {
return nil, errors.Annotate(err, "failed to create the new HNSEndpoint")
}
err = hcn.AddNamespaceEndpoint(namespace, hcnEndpoint.Id)
if err != nil {
err := RemoveHcnEndpoint(epName)
if err != nil {
return nil, errors.Annotatef(err, "failed to Remove Endpoint after AddNamespaceEndpoint failure")
}
return nil, errors.Annotate(err, "failed to Add endpoint to namespace")
}
return hcnEndpoint, nil
}
// ConstructResult constructs the CNI result for the endpoint
func ConstructResult(hnsNetwork *hcsshim.HNSNetwork, hnsEndpoint *hcsshim.HNSEndpoint) (*current.Result, error) {
resultInterface := &current.Interface{
Name: hnsEndpoint.Name,
Mac: hnsEndpoint.MacAddress,
}
_, ipSubnet, err := net.ParseCIDR(hnsNetwork.Subnets[0].AddressPrefix)
if err != nil {
return nil, errors.Annotatef(err, "failed to parse CIDR from %s", hnsNetwork.Subnets[0].AddressPrefix)
}
var ipVersion string
if ipv4 := hnsEndpoint.IPAddress.To4(); ipv4 != nil {
ipVersion = "4"
} else if ipv6 := hnsEndpoint.IPAddress.To16(); ipv6 != nil {
ipVersion = "6"
} else {
return nil, fmt.Errorf("IPAddress of HNSEndpoint %s isn't a valid ipv4 or ipv6 Address", hnsEndpoint.Name)
}
resultIPConfig := &current.IPConfig{
Version: ipVersion,
Address: net.IPNet{
IP: hnsEndpoint.IPAddress,
Mask: ipSubnet.Mask},
Gateway: net.ParseIP(hnsEndpoint.GatewayAddress),
}
result := &current.Result{}
result.Interfaces = []*current.Interface{resultInterface}
result.IPs = []*current.IPConfig{resultIPConfig}
result.DNS = types.DNS{
Search: strings.Split(hnsEndpoint.DNSSuffix, ","),
Nameservers: strings.Split(hnsEndpoint.DNSServerList, ","),
}
return result, nil
}
// This version follows the v2 workflow of removing the endpoint from the namespace and deleting it
func RemoveHcnEndpoint(epName string) error {
hcnEndpoint, err := hcn.GetEndpointByName(epName)
if hcn.IsNotFoundError(err) {
return nil
} else if err != nil {
_ = fmt.Errorf("[win-cni] Failed to find endpoint %v, err:%v", epName, err)
return err
}
if hcnEndpoint != nil {
err = hcnEndpoint.Delete()
if err != nil {
return fmt.Errorf("[win-cni] Failed to delete endpoint %v, err:%v", epName, err)
}
}
return nil
}
func ConstructHcnResult(hcnNetwork *hcn.HostComputeNetwork, hcnEndpoint *hcn.HostComputeEndpoint) (*current.Result, error) {
resultInterface := &current.Interface{
Name: hcnEndpoint.Name,
Mac: hcnEndpoint.MacAddress,
}
_, ipSubnet, err := net.ParseCIDR(hcnNetwork.Ipams[0].Subnets[0].IpAddressPrefix)
if err != nil {
return nil, err
}
var ipVersion string
ipAddress := net.ParseIP(hcnEndpoint.IpConfigurations[0].IpAddress)
if ipv4 := ipAddress.To4(); ipv4 != nil {
ipVersion = "4"
} else if ipv6 := ipAddress.To16(); ipv6 != nil {
ipVersion = "6"
} else {
return nil, fmt.Errorf("[win-cni] The IPAddress of hnsEndpoint isn't a valid ipv4 or ipv6 Address.")
}
resultIPConfig := &current.IPConfig{
Version: ipVersion,
Address: net.IPNet{
IP: ipAddress,
Mask: ipSubnet.Mask},
Gateway: net.ParseIP(hcnEndpoint.Routes[0].NextHop),
}
result := &current.Result{}
result.Interfaces = []*current.Interface{resultInterface}
result.IPs = []*current.IPConfig{resultIPConfig}
result.DNS = types.DNS{
Search: hcnEndpoint.Dns.Search,
Nameservers: hcnEndpoint.Dns.ServerList,
}
return result, nil
}

View File

@ -0,0 +1,13 @@
package hns_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestHns(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Hns Suite")
}

View File

@ -0,0 +1,26 @@
// Copyright 2017 CNI authors
//
// 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.
package hns
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
)
func TestHns(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "HNS NetConf Suite")
}

236
pkg/hns/netconf_windows.go Normal file
View File

@ -0,0 +1,236 @@
// Copyright 2017 CNI authors
//
// 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.
package hns
import (
"bytes"
"encoding/json"
"fmt"
"net"
"strings"
"github.com/Microsoft/hcsshim/hcn"
"github.com/buger/jsonparser"
"github.com/containernetworking/cni/pkg/types"
)
// NetConf is the CNI spec
type NetConf struct {
types.NetConf
// ApiVersion is either 1 or 2, which specifies which hns APIs to call
ApiVersion int `json:"ApiVersion"`
// V2 Api Policies
HcnPolicyArgs []hcn.EndpointPolicy `json:"HcnPolicyArgs,omitempty"`
// V1 Api Policies
Policies []policy `json:"policies,omitempty"`
// Options to be passed in by the runtime
RuntimeConfig RuntimeConfig `json:"runtimeConfig"`
// If true, adds a policy to endpoints to support loopback direct server return
LoopbackDSR bool `json:"loopbackDSR"`
}
type RuntimeDNS struct {
Nameservers []string `json:"servers,omitempty"`
Search []string `json:"searches,omitempty"`
}
type PortMapEntry struct {
HostPort int `json:"hostPort"`
ContainerPort int `json:"containerPort"`
Protocol string `json:"protocol"`
HostIP string `json:"hostIP,omitempty"`
}
type RuntimeConfig struct {
DNS RuntimeDNS `json:"dns"`
PortMaps []PortMapEntry `json:"portMappings,omitempty"`
}
type policy struct {
Name string `json:"name"`
Value json.RawMessage `json:"value"`
}
func GetDefaultDestinationPrefix(ip *net.IP) string {
destinationPrefix := "0.0.0.0/0"
if ipv6 := ip.To4(); ipv6 == nil {
destinationPrefix = "::/0"
}
return destinationPrefix
}
func (n *NetConf) ApplyLoopbackDSR(ip *net.IP) {
value := fmt.Sprintf(`"Destinations" : ["%s"]`, ip.String())
if n.ApiVersion == 2 {
hcnLoopbackRoute := hcn.EndpointPolicy{
Type: "OutBoundNAT",
Settings: []byte(fmt.Sprintf("{%s}", value)),
}
n.HcnPolicyArgs = append(n.HcnPolicyArgs, hcnLoopbackRoute)
} else {
hnsLoopbackRoute := policy{
Name: "EndpointPolicy",
Value: []byte(fmt.Sprintf(`{"Type": "OutBoundNAT", %s}`, value)),
}
n.Policies = append(n.Policies, hnsLoopbackRoute)
}
}
// If runtime dns values are there use that else use cni conf supplied dns
func (n *NetConf) GetDNS() types.DNS {
dnsResult := n.DNS
if len(n.RuntimeConfig.DNS.Nameservers) > 0 {
dnsResult.Nameservers = n.RuntimeConfig.DNS.Nameservers
}
if len(n.RuntimeConfig.DNS.Search) > 0 {
dnsResult.Search = n.RuntimeConfig.DNS.Search
}
return dnsResult
}
// MarshalPolicies converts the Endpoint policies in Policies
// to HNS specific policies as Json raw bytes
func (n *NetConf) MarshalPolicies() []json.RawMessage {
if n.Policies == nil {
n.Policies = make([]policy, 0)
}
result := make([]json.RawMessage, 0, len(n.Policies))
for _, p := range n.Policies {
if !strings.EqualFold(p.Name, "EndpointPolicy") {
continue
}
result = append(result, p.Value)
}
return result
}
// ApplyOutboundNatPolicy applies NAT Policy in VFP using HNS
// Simultaneously an exception is added for the network that has to be Nat'd
func (n *NetConf) ApplyOutboundNatPolicy(nwToNat string) {
if n.Policies == nil {
n.Policies = make([]policy, 0)
}
nwToNatBytes := []byte(nwToNat)
for i, p := range n.Policies {
if !strings.EqualFold(p.Name, "EndpointPolicy") {
continue
}
typeValue, err := jsonparser.GetUnsafeString(p.Value, "Type")
if err != nil || len(typeValue) == 0 {
continue
}
if !strings.EqualFold(typeValue, "OutBoundNAT") {
continue
}
exceptionListValue, dt, _, _ := jsonparser.Get(p.Value, "ExceptionList")
// OutBoundNAT must with ExceptionList, so don't need to judge jsonparser.NotExist
if dt == jsonparser.Array {
buf := bytes.Buffer{}
buf.WriteString(`{"Type": "OutBoundNAT", "ExceptionList": [`)
jsonparser.ArrayEach(exceptionListValue, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
if dataType == jsonparser.String && len(value) != 0 {
if bytes.Compare(value, nwToNatBytes) != 0 {
buf.WriteByte('"')
buf.Write(value)
buf.WriteByte('"')
buf.WriteByte(',')
}
}
})
buf.WriteString(`"` + nwToNat + `"]}`)
n.Policies[i] = policy{
Name: "EndpointPolicy",
Value: buf.Bytes(),
}
} else {
n.Policies[i] = policy{
Name: "EndpointPolicy",
Value: []byte(`{"Type": "OutBoundNAT", "ExceptionList": ["` + nwToNat + `"]}`),
}
}
return
}
// didn't find the policyArg, add it
n.Policies = append(n.Policies, policy{
Name: "EndpointPolicy",
Value: []byte(`{"Type": "OutBoundNAT", "ExceptionList": ["` + nwToNat + `"]}`),
})
}
// ApplyDefaultPAPolicy is used to configure a endpoint PA policy in HNS
func (n *NetConf) ApplyDefaultPAPolicy(paAddress string) {
if n.Policies == nil {
n.Policies = make([]policy, 0)
}
// if its already present, leave untouched
for i, p := range n.Policies {
if !strings.EqualFold(p.Name, "EndpointPolicy") {
continue
}
paValue, dt, _, _ := jsonparser.Get(p.Value, "PA")
if dt == jsonparser.NotExist {
continue
} else if dt == jsonparser.String && len(paValue) != 0 {
// found it, don't override
return
}
n.Policies[i] = policy{
Name: "EndpointPolicy",
Value: []byte(`{"Type": "PA", "PA": "` + paAddress + `"}`),
}
return
}
// didn't find the policyArg, add it
n.Policies = append(n.Policies, policy{
Name: "EndpointPolicy",
Value: []byte(`{"Type": "PA", "PA": "` + paAddress + `"}`),
})
}
// ApplyPortMappingPolicy is used to configure HostPort<>ContainerPort mapping in HNS
func (n *NetConf) ApplyPortMappingPolicy(portMappings []PortMapEntry) {
if portMappings == nil {
return
}
if n.Policies == nil {
n.Policies = make([]policy, 0)
}
for _, portMapping := range portMappings {
n.Policies = append(n.Policies, policy{
Name: "EndpointPolicy",
Value: []byte(fmt.Sprintf(`{"Type": "NAT", "InternalPort": %d, "ExternalPort": %d, "Protocol": "%s"}`, portMapping.ContainerPort, portMapping.HostPort, portMapping.Protocol)),
})
}
}

View File

@ -0,0 +1,236 @@
// Copyright 2017 CNI authors
//
// 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.
package hns
import (
"encoding/json"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("HNS NetConf", func() {
Describe("ApplyOutBoundNATPolicy", func() {
Context("when not set by user", func() {
It("sets it by adding a policy", func() {
// apply it
n := NetConf{}
n.ApplyOutboundNatPolicy("192.168.0.0/16")
addlArgs := n.Policies
Expect(addlArgs).Should(HaveLen(1))
policy := addlArgs[0]
Expect(policy.Name).Should(Equal("EndpointPolicy"))
value := make(map[string]interface{})
json.Unmarshal(policy.Value, &value)
Expect(value).Should(HaveKey("Type"))
Expect(value).Should(HaveKey("ExceptionList"))
Expect(value["Type"]).Should(Equal("OutBoundNAT"))
exceptionList := value["ExceptionList"].([]interface{})
Expect(exceptionList).Should(HaveLen(1))
Expect(exceptionList[0].(string)).Should(Equal("192.168.0.0/16"))
})
})
Context("when set by user", func() {
It("appends exceptions to the existing policy", func() {
// first set it
n := NetConf{}
n.ApplyOutboundNatPolicy("192.168.0.0/16")
// then attempt to update it
n.ApplyOutboundNatPolicy("10.244.0.0/16")
// it should be unchanged!
addlArgs := n.Policies
Expect(addlArgs).Should(HaveLen(1))
policy := addlArgs[0]
Expect(policy.Name).Should(Equal("EndpointPolicy"))
var value map[string]interface{}
json.Unmarshal(policy.Value, &value)
Expect(value).Should(HaveKey("Type"))
Expect(value).Should(HaveKey("ExceptionList"))
Expect(value["Type"]).Should(Equal("OutBoundNAT"))
exceptionList := value["ExceptionList"].([]interface{})
Expect(exceptionList).Should(HaveLen(2))
Expect(exceptionList[0].(string)).Should(Equal("192.168.0.0/16"))
Expect(exceptionList[1].(string)).Should(Equal("10.244.0.0/16"))
})
})
})
Describe("ApplyDefaultPAPolicy", func() {
Context("when not set by user", func() {
It("sets it by adding a policy", func() {
n := NetConf{}
n.ApplyDefaultPAPolicy("192.168.0.1")
addlArgs := n.Policies
Expect(addlArgs).Should(HaveLen(1))
policy := addlArgs[0]
Expect(policy.Name).Should(Equal("EndpointPolicy"))
value := make(map[string]interface{})
json.Unmarshal(policy.Value, &value)
Expect(value).Should(HaveKey("Type"))
Expect(value["Type"]).Should(Equal("PA"))
paAddress := value["PA"].(string)
Expect(paAddress).Should(Equal("192.168.0.1"))
})
})
Context("when set by user", func() {
It("does not override", func() {
n := NetConf{}
n.ApplyDefaultPAPolicy("192.168.0.1")
n.ApplyDefaultPAPolicy("192.168.0.2")
addlArgs := n.Policies
Expect(addlArgs).Should(HaveLen(1))
policy := addlArgs[0]
Expect(policy.Name).Should(Equal("EndpointPolicy"))
value := make(map[string]interface{})
json.Unmarshal(policy.Value, &value)
Expect(value).Should(HaveKey("Type"))
Expect(value["Type"]).Should(Equal("PA"))
paAddress := value["PA"].(string)
Expect(paAddress).Should(Equal("192.168.0.1"))
Expect(paAddress).ShouldNot(Equal("192.168.0.2"))
})
})
})
Describe("ApplyPortMappingPolicy", func() {
Context("when portMappings not activated", func() {
It("does nothing", func() {
n := NetConf{}
n.ApplyPortMappingPolicy(nil)
Expect(n.Policies).Should(BeNil())
n.ApplyPortMappingPolicy([]PortMapEntry{})
Expect(n.Policies).Should(HaveLen(0))
})
})
Context("when portMappings is activated", func() {
It("creates NAT policies", func() {
n := NetConf{}
n.ApplyPortMappingPolicy([]PortMapEntry{
{
ContainerPort: 80,
HostPort: 8080,
Protocol: "TCP",
HostIP: "ignored",
},
})
Expect(n.Policies).Should(HaveLen(1))
policy := n.Policies[0]
Expect(policy.Name).Should(Equal("EndpointPolicy"))
value := make(map[string]interface{})
json.Unmarshal(policy.Value, &value)
Expect(value).Should(HaveKey("Type"))
Expect(value["Type"]).Should(Equal("NAT"))
Expect(value).Should(HaveKey("InternalPort"))
Expect(value["InternalPort"]).Should(Equal(float64(80)))
Expect(value).Should(HaveKey("ExternalPort"))
Expect(value["ExternalPort"]).Should(Equal(float64(8080)))
Expect(value).Should(HaveKey("Protocol"))
Expect(value["Protocol"]).Should(Equal("TCP"))
})
})
})
Describe("MarshalPolicies", func() {
Context("when not set by user", func() {
It("sets it by adding a policy", func() {
n := NetConf{
Policies: []policy{
{
Name: "EndpointPolicy",
Value: []byte(`{"someKey": "someValue"}`),
},
{
Name: "someOtherType",
Value: []byte(`{"someOtherKey": "someOtherValue"}`),
},
},
}
result := n.MarshalPolicies()
Expect(len(result)).To(Equal(1))
policy := make(map[string]interface{})
err := json.Unmarshal(result[0], &policy)
Expect(err).ToNot(HaveOccurred())
Expect(policy).Should(HaveKey("someKey"))
Expect(policy["someKey"]).To(Equal("someValue"))
})
})
Context("when set by user", func() {
It("appends exceptions to the existing policy", func() {
// first set it
n := NetConf{}
n.ApplyOutboundNatPolicy("192.168.0.0/16")
// then attempt to update it
n.ApplyOutboundNatPolicy("10.244.0.0/16")
// it should be unchanged!
addlArgs := n.Policies
Expect(addlArgs).Should(HaveLen(1))
policy := addlArgs[0]
Expect(policy.Name).Should(Equal("EndpointPolicy"))
var value map[string]interface{}
json.Unmarshal(policy.Value, &value)
Expect(value).Should(HaveKey("Type"))
Expect(value).Should(HaveKey("ExceptionList"))
Expect(value["Type"]).Should(Equal("OutBoundNAT"))
exceptionList := value["ExceptionList"].([]interface{})
Expect(exceptionList).Should(HaveLen(2))
Expect(exceptionList[0].(string)).Should(Equal("192.168.0.0/16"))
Expect(exceptionList[1].(string)).Should(Equal("10.244.0.0/16"))
})
})
})
})

View File

@ -1,53 +0,0 @@
// Copyright 2016 CNI authors
//
// 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.
package invoke
import (
"fmt"
"os"
"strings"
"github.com/containernetworking/cni/pkg/types"
)
func DelegateAdd(delegatePlugin string, netconf []byte) (*types.Result, error) {
if os.Getenv("CNI_COMMAND") != "ADD" {
return nil, fmt.Errorf("CNI_COMMAND is not ADD")
}
paths := strings.Split(os.Getenv("CNI_PATH"), ":")
pluginPath, err := FindInPath(delegatePlugin, paths)
if err != nil {
return nil, err
}
return ExecPluginWithResult(pluginPath, netconf, ArgsFromEnv())
}
func DelegateDel(delegatePlugin string, netconf []byte) error {
if os.Getenv("CNI_COMMAND") != "DEL" {
return fmt.Errorf("CNI_COMMAND is not DEL")
}
paths := strings.Split(os.Getenv("CNI_PATH"), ":")
pluginPath, err := FindInPath(delegatePlugin, paths)
if err != nil {
return err
}
return ExecPluginWithoutResult(pluginPath, netconf, ArgsFromEnv())
}

View File

@ -1,75 +0,0 @@
// Copyright 2015 CNI authors
//
// 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.
package invoke
import (
"bytes"
"encoding/json"
"fmt"
"os"
"os/exec"
"github.com/containernetworking/cni/pkg/types"
)
func pluginErr(err error, output []byte) error {
if _, ok := err.(*exec.ExitError); ok {
emsg := types.Error{}
if perr := json.Unmarshal(output, &emsg); perr != nil {
return fmt.Errorf("netplugin failed but error parsing its diagnostic message %q: %v", string(output), perr)
}
details := ""
if emsg.Details != "" {
details = fmt.Sprintf("; %v", emsg.Details)
}
return fmt.Errorf("%v%v", emsg.Msg, details)
}
return err
}
func ExecPluginWithResult(pluginPath string, netconf []byte, args CNIArgs) (*types.Result, error) {
stdoutBytes, err := execPlugin(pluginPath, netconf, args)
if err != nil {
return nil, err
}
res := &types.Result{}
err = json.Unmarshal(stdoutBytes, res)
return res, err
}
func ExecPluginWithoutResult(pluginPath string, netconf []byte, args CNIArgs) error {
_, err := execPlugin(pluginPath, netconf, args)
return err
}
func execPlugin(pluginPath string, netconf []byte, args CNIArgs) ([]byte, error) {
stdout := &bytes.Buffer{}
c := exec.Cmd{
Env: args.AsEnv(),
Path: pluginPath,
Args: []string{pluginPath},
Stdin: bytes.NewBuffer(netconf),
Stdout: stdout,
Stderr: os.Stderr,
}
if err := c.Run(); err != nil {
return nil, pluginErr(err, stdout.Bytes())
}
return stdout.Bytes(), nil
}

View File

@ -1,78 +0,0 @@
// Copyright 2016 CNI authors
//
// 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.
package invoke_test
import (
"fmt"
"io/ioutil"
"path/filepath"
"github.com/containernetworking/cni/pkg/invoke"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("FindInPath", func() {
var (
multiplePaths []string
pluginName string
pluginDir string
anotherTempDir string
)
BeforeEach(func() {
tempDir, err := ioutil.TempDir("", "cni-find")
Expect(err).NotTo(HaveOccurred())
plugin, err := ioutil.TempFile(tempDir, "a-cni-plugin")
anotherTempDir, err = ioutil.TempDir("", "nothing-here")
multiplePaths = []string{anotherTempDir, tempDir}
pluginDir, pluginName = filepath.Split(plugin.Name())
})
Context("when multiple paths are provided", func() {
It("returns only the path to the plugin", func() {
pluginPath, err := invoke.FindInPath(pluginName, multiplePaths)
Expect(err).NotTo(HaveOccurred())
Expect(pluginPath).To(Equal(filepath.Join(pluginDir, pluginName)))
})
})
Context("when an error occurs", func() {
Context("when no paths are provided", func() {
It("returns an error noting no paths were provided", func() {
_, err := invoke.FindInPath(pluginName, []string{})
Expect(err).To(MatchError("no paths provided"))
})
})
Context("when no plugin is provided", func() {
It("returns an error noting the plugin name wasn't found", func() {
_, err := invoke.FindInPath("", multiplePaths)
Expect(err).To(MatchError("no plugin name provided"))
})
})
Context("when the plugin cannot be found", func() {
It("returns an error noting the path", func() {
pathsWithNothing := []string{anotherTempDir}
_, err := invoke.FindInPath(pluginName, pathsWithNothing)
Expect(err).To(MatchError(fmt.Sprintf("failed to find plugin %q in path %s", pluginName, pathsWithNothing)))
})
})
})
})

68
pkg/ip/addr_linux.go Normal file
View File

@ -0,0 +1,68 @@
// Copyright 2017 CNI authors
//
// 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.
package ip
import (
"fmt"
"syscall"
"time"
"github.com/vishvananda/netlink"
)
const SETTLE_INTERVAL = 50 * time.Millisecond
// SettleAddresses waits for all addresses on a link to leave tentative state.
// This is particularly useful for ipv6, where all addresses need to do DAD.
// There is no easy way to wait for this as an event, so just loop until the
// addresses are no longer tentative.
// If any addresses are still tentative after timeout seconds, then error.
func SettleAddresses(ifName string, timeout int) error {
link, err := netlink.LinkByName(ifName)
if err != nil {
return fmt.Errorf("failed to retrieve link: %v", err)
}
deadline := time.Now().Add(time.Duration(timeout) * time.Second)
for {
addrs, err := netlink.AddrList(link, netlink.FAMILY_ALL)
if err != nil {
return fmt.Errorf("could not list addresses: %v", err)
}
if len(addrs) == 0 {
return nil
}
ok := true
for _, addr := range addrs {
if addr.Flags&(syscall.IFA_F_TENTATIVE|syscall.IFA_F_DADFAILED) > 0 {
ok = false
break // Break out of the `range addrs`, not the `for`
}
}
if ok {
return nil
}
if time.Now().After(deadline) {
return fmt.Errorf("link %s still has tentative addresses after %d seconds",
ifName,
timeout)
}
time.Sleep(SETTLE_INTERVAL)
}
}

View File

@ -31,6 +31,16 @@ func PrevIP(ip net.IP) net.IP {
return intToIP(i.Sub(i, big.NewInt(1)))
}
// Cmp compares two IPs, returning the usual ordering:
// a < b : -1
// a == b : 0
// a > b : 1
func Cmp(a, b net.IP) int {
aa := ipToInt(a)
bb := ipToInt(b)
return aa.Cmp(bb)
}
func ipToInt(ip net.IP) *big.Int {
if v := ip.To4(); v != nil {
return big.NewInt(0).SetBytes(v)

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package skel
package ip_test
import (
. "github.com/onsi/ginkgo"
@ -21,7 +21,7 @@ import (
"testing"
)
func TestSkel(t *testing.T) {
func TestIp(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Skel Suite")
RunSpecs(t, "pkg/ip")
}

61
pkg/ip/ipforward_linux.go Normal file
View File

@ -0,0 +1,61 @@
// Copyright 2015 CNI authors
//
// 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.
package ip
import (
"bytes"
"io/ioutil"
"github.com/containernetworking/cni/pkg/types/current"
)
func EnableIP4Forward() error {
return echo1("/proc/sys/net/ipv4/ip_forward")
}
func EnableIP6Forward() error {
return echo1("/proc/sys/net/ipv6/conf/all/forwarding")
}
// EnableForward will enable forwarding for all configured
// address families
func EnableForward(ips []*current.IPConfig) error {
v4 := false
v6 := false
for _, ip := range ips {
if ip.Version == "4" && !v4 {
if err := EnableIP4Forward(); err != nil {
return err
}
v4 = true
} else if ip.Version == "6" && !v6 {
if err := EnableIP6Forward(); err != nil {
return err
}
v6 = true
}
}
return nil
}
func echo1(f string) error {
if content, err := ioutil.ReadFile(f); err == nil {
if bytes.Equal(bytes.TrimSpace(content), []byte("1")) {
return nil
}
}
return ioutil.WriteFile(f, []byte("1"), 0644)
}

View File

@ -0,0 +1,32 @@
package ip
import (
"io/ioutil"
"os"
"time"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("IpforwardLinux", func() {
It("echo1 must not write the file if content is 1", func() {
file, err := ioutil.TempFile(os.TempDir(), "containernetworking")
Expect(err).NotTo(HaveOccurred())
defer os.Remove(file.Name())
err = echo1(file.Name())
Expect(err).NotTo(HaveOccurred())
statBefore, err := file.Stat()
Expect(err).NotTo(HaveOccurred())
// take a duration here, otherwise next file modification operation time
// will be same as previous one.
time.Sleep(100 * time.Millisecond)
err = echo1(file.Name())
Expect(err).NotTo(HaveOccurred())
statAfter, err := file.Stat()
Expect(err).NotTo(HaveOccurred())
Expect(statBefore.ModTime()).To(Equal(statAfter.ModTime()))
})
})

View File

@ -1,66 +0,0 @@
// Copyright 2015 CNI authors
//
// 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.
package ip
import (
"fmt"
"net"
"github.com/coreos/go-iptables/iptables"
)
// SetupIPMasq installs iptables rules to masquerade traffic
// coming from ipn and going outside of it
func SetupIPMasq(ipn *net.IPNet, chain string, comment string) error {
ipt, err := iptables.New()
if err != nil {
return fmt.Errorf("failed to locate iptables: %v", err)
}
if err = ipt.NewChain("nat", chain); err != nil {
if err.(*iptables.Error).ExitStatus() != 1 {
// TODO(eyakubovich): assumes exit status 1 implies chain exists
return err
}
}
if err = ipt.AppendUnique("nat", chain, "-d", ipn.String(), "-j", "ACCEPT", "-m", "comment", "--comment", comment); err != nil {
return err
}
if err = ipt.AppendUnique("nat", chain, "!", "-d", "224.0.0.0/4", "-j", "MASQUERADE", "-m", "comment", "--comment", comment); err != nil {
return err
}
return ipt.AppendUnique("nat", "POSTROUTING", "-s", ipn.String(), "-j", chain, "-m", "comment", "--comment", comment)
}
// TeardownIPMasq undoes the effects of SetupIPMasq
func TeardownIPMasq(ipn *net.IPNet, chain string, comment string) error {
ipt, err := iptables.New()
if err != nil {
return fmt.Errorf("failed to locate iptables: %v", err)
}
if err = ipt.Delete("nat", "POSTROUTING", "-s", ipn.String(), "-j", chain, "-m", "comment", "--comment", comment); err != nil {
return err
}
if err = ipt.ClearChain("nat", chain); err != nil {
return err
}
return ipt.DeleteChain("nat", chain)
}

126
pkg/ip/ipmasq_linux.go Normal file
View File

@ -0,0 +1,126 @@
// Copyright 2015 CNI authors
//
// 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.
package ip
import (
"fmt"
"net"
"github.com/coreos/go-iptables/iptables"
)
// SetupIPMasq installs iptables rules to masquerade traffic
// coming from ip of ipn and going outside of ipn
func SetupIPMasq(ipn *net.IPNet, chain string, comment string) error {
isV6 := ipn.IP.To4() == nil
var ipt *iptables.IPTables
var err error
var multicastNet string
if isV6 {
ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv6)
multicastNet = "ff00::/8"
} else {
ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv4)
multicastNet = "224.0.0.0/4"
}
if err != nil {
return fmt.Errorf("failed to locate iptables: %v", err)
}
// Create chain if doesn't exist
exists := false
chains, err := ipt.ListChains("nat")
if err != nil {
return fmt.Errorf("failed to list chains: %v", err)
}
for _, ch := range chains {
if ch == chain {
exists = true
break
}
}
if !exists {
if err = ipt.NewChain("nat", chain); err != nil {
return err
}
}
// Packets to this network should not be touched
if err := ipt.AppendUnique("nat", chain, "-d", ipn.String(), "-j", "ACCEPT", "-m", "comment", "--comment", comment); err != nil {
return err
}
// Don't masquerade multicast - pods should be able to talk to other pods
// on the local network via multicast.
if err := ipt.AppendUnique("nat", chain, "!", "-d", multicastNet, "-j", "MASQUERADE", "-m", "comment", "--comment", comment); err != nil {
return err
}
// Packets from the specific IP of this network will hit the chain
return ipt.AppendUnique("nat", "POSTROUTING", "-s", ipn.IP.String(), "-j", chain, "-m", "comment", "--comment", comment)
}
// TeardownIPMasq undoes the effects of SetupIPMasq
func TeardownIPMasq(ipn *net.IPNet, chain string, comment string) error {
isV6 := ipn.IP.To4() == nil
var ipt *iptables.IPTables
var err error
if isV6 {
ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv6)
} else {
ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv4)
}
if err != nil {
return fmt.Errorf("failed to locate iptables: %v", err)
}
err = ipt.Delete("nat", "POSTROUTING", "-s", ipn.IP.String(), "-j", chain, "-m", "comment", "--comment", comment)
if err != nil && !isNotExist(err) {
return err
}
// for downward compatibility
err = ipt.Delete("nat", "POSTROUTING", "-s", ipn.String(), "-j", chain, "-m", "comment", "--comment", comment)
if err != nil && !isNotExist(err) {
return err
}
err = ipt.ClearChain("nat", chain)
if err != nil && !isNotExist(err) {
return err
}
err = ipt.DeleteChain("nat", chain)
if err != nil && !isNotExist(err) {
return err
}
return nil
}
// isNotExist returnst true if the error is from iptables indicating
// that the target does not exist.
func isNotExist(err error) bool {
e, ok := err.(*iptables.Error)
if !ok {
return false
}
return e.IsNotExist()
}

View File

@ -1,153 +0,0 @@
// Copyright 2015 CNI authors
//
// 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.
package ip
import (
"crypto/rand"
"fmt"
"net"
"os"
"github.com/containernetworking/cni/pkg/ns"
"github.com/vishvananda/netlink"
)
func makeVethPair(name, peer string, mtu int) (netlink.Link, error) {
veth := &netlink.Veth{
LinkAttrs: netlink.LinkAttrs{
Name: name,
Flags: net.FlagUp,
MTU: mtu,
},
PeerName: peer,
}
if err := netlink.LinkAdd(veth); err != nil {
return nil, err
}
return veth, nil
}
func makeVeth(name string, mtu int) (peerName string, veth netlink.Link, err error) {
for i := 0; i < 10; i++ {
peerName, err = RandomVethName()
if err != nil {
return
}
veth, err = makeVethPair(name, peerName, mtu)
switch {
case err == nil:
return
case os.IsExist(err):
continue
default:
err = fmt.Errorf("failed to make veth pair: %v", err)
return
}
}
// should really never be hit
err = fmt.Errorf("failed to find a unique veth name")
return
}
// RandomVethName returns string "veth" with random prefix (hashed from entropy)
func RandomVethName() (string, error) {
entropy := make([]byte, 4)
_, err := rand.Reader.Read(entropy)
if err != nil {
return "", fmt.Errorf("failed to generate random veth name: %v", err)
}
// NetworkManager (recent versions) will ignore veth devices that start with "veth"
return fmt.Sprintf("veth%x", entropy), nil
}
// SetupVeth sets up a virtual ethernet link.
// Should be in container netns, and will switch back to hostNS to set the host
// veth end up.
func SetupVeth(contVethName string, mtu int, hostNS ns.NetNS) (hostVeth, contVeth netlink.Link, err error) {
var hostVethName string
hostVethName, contVeth, err = makeVeth(contVethName, mtu)
if err != nil {
return
}
if err = netlink.LinkSetUp(contVeth); err != nil {
err = fmt.Errorf("failed to set %q up: %v", contVethName, err)
return
}
hostVeth, err = netlink.LinkByName(hostVethName)
if err != nil {
err = fmt.Errorf("failed to lookup %q: %v", hostVethName, err)
return
}
if err = netlink.LinkSetNsFd(hostVeth, int(hostNS.Fd())); err != nil {
err = fmt.Errorf("failed to move veth to host netns: %v", err)
return
}
err = hostNS.Do(func(_ ns.NetNS) error {
hostVeth, err := netlink.LinkByName(hostVethName)
if err != nil {
return fmt.Errorf("failed to lookup %q in %q: %v", hostVethName, hostNS.Path(), err)
}
if err = netlink.LinkSetUp(hostVeth); err != nil {
return fmt.Errorf("failed to set %q up: %v", hostVethName, err)
}
return nil
})
return
}
// DelLinkByName removes an interface link.
func DelLinkByName(ifName string) error {
iface, err := netlink.LinkByName(ifName)
if err != nil {
return fmt.Errorf("failed to lookup %q: %v", ifName, err)
}
if err = netlink.LinkDel(iface); err != nil {
return fmt.Errorf("failed to delete %q: %v", ifName, err)
}
return nil
}
// DelLinkByNameAddr remove an interface returns its IP address
// of the specified family
func DelLinkByNameAddr(ifName string, family int) (*net.IPNet, error) {
iface, err := netlink.LinkByName(ifName)
if err != nil {
return nil, fmt.Errorf("failed to lookup %q: %v", ifName, err)
}
addrs, err := netlink.AddrList(iface, family)
if err != nil || len(addrs) == 0 {
return nil, fmt.Errorf("failed to get IP addresses for %q: %v", ifName, err)
}
if err = netlink.LinkDel(iface); err != nil {
return nil, fmt.Errorf("failed to delete %q: %v", ifName, err)
}
return addrs[0].IPNet, nil
}

293
pkg/ip/link_linux.go Normal file
View File

@ -0,0 +1,293 @@
// Copyright 2015 CNI authors
//
// 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.
package ip
import (
"crypto/rand"
"errors"
"fmt"
"net"
"os"
"github.com/safchain/ethtool"
"github.com/vishvananda/netlink"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/utils/hwaddr"
"github.com/containernetworking/plugins/pkg/utils/sysctl"
)
var (
ErrLinkNotFound = errors.New("link not found")
)
func makeVethPair(name, peer string, mtu int) (netlink.Link, error) {
veth := &netlink.Veth{
LinkAttrs: netlink.LinkAttrs{
Name: name,
Flags: net.FlagUp,
MTU: mtu,
},
PeerName: peer,
}
if err := netlink.LinkAdd(veth); err != nil {
return nil, err
}
// Re-fetch the link to get its creation-time parameters, e.g. index and mac
veth2, err := netlink.LinkByName(name)
if err != nil {
netlink.LinkDel(veth) // try and clean up the link if possible.
return nil, err
}
return veth2, nil
}
func peerExists(name string) bool {
if _, err := netlink.LinkByName(name); err != nil {
return false
}
return true
}
func makeVeth(name, vethPeerName string, mtu int) (peerName string, veth netlink.Link, err error) {
for i := 0; i < 10; i++ {
if vethPeerName != "" {
peerName = vethPeerName
} else {
peerName, err = RandomVethName()
if err != nil {
return
}
}
veth, err = makeVethPair(name, peerName, mtu)
switch {
case err == nil:
return
case os.IsExist(err):
if peerExists(peerName) && vethPeerName == "" {
continue
}
err = fmt.Errorf("container veth name provided (%v) already exists", name)
return
default:
err = fmt.Errorf("failed to make veth pair: %v", err)
return
}
}
// should really never be hit
err = fmt.Errorf("failed to find a unique veth name")
return
}
// RandomVethName returns string "veth" with random prefix (hashed from entropy)
func RandomVethName() (string, error) {
entropy := make([]byte, 4)
_, err := rand.Reader.Read(entropy)
if err != nil {
return "", fmt.Errorf("failed to generate random veth name: %v", err)
}
// NetworkManager (recent versions) will ignore veth devices that start with "veth"
return fmt.Sprintf("veth%x", entropy), nil
}
func RenameLink(curName, newName string) error {
link, err := netlink.LinkByName(curName)
if err == nil {
err = netlink.LinkSetName(link, newName)
}
return err
}
func ifaceFromNetlinkLink(l netlink.Link) net.Interface {
a := l.Attrs()
return net.Interface{
Index: a.Index,
MTU: a.MTU,
Name: a.Name,
HardwareAddr: a.HardwareAddr,
Flags: a.Flags,
}
}
// SetupVethWithName sets up a pair of virtual ethernet devices.
// Call SetupVethWithName from inside the container netns. It will create both veth
// devices and move the host-side veth into the provided hostNS namespace.
// hostVethName: If hostVethName is not specified, the host-side veth name will use a random string.
// On success, SetupVethWithName returns (hostVeth, containerVeth, nil)
func SetupVethWithName(contVethName, hostVethName string, mtu int, hostNS ns.NetNS) (net.Interface, net.Interface, error) {
hostVethName, contVeth, err := makeVeth(contVethName, hostVethName, mtu)
if err != nil {
return net.Interface{}, net.Interface{}, err
}
if err = netlink.LinkSetUp(contVeth); err != nil {
return net.Interface{}, net.Interface{}, fmt.Errorf("failed to set %q up: %v", contVethName, err)
}
hostVeth, err := netlink.LinkByName(hostVethName)
if err != nil {
return net.Interface{}, net.Interface{}, fmt.Errorf("failed to lookup %q: %v", hostVethName, err)
}
if err = netlink.LinkSetNsFd(hostVeth, int(hostNS.Fd())); err != nil {
return net.Interface{}, net.Interface{}, fmt.Errorf("failed to move veth to host netns: %v", err)
}
err = hostNS.Do(func(_ ns.NetNS) error {
hostVeth, err = netlink.LinkByName(hostVethName)
if err != nil {
return fmt.Errorf("failed to lookup %q in %q: %v", hostVethName, hostNS.Path(), err)
}
if err = netlink.LinkSetUp(hostVeth); err != nil {
return fmt.Errorf("failed to set %q up: %v", hostVethName, err)
}
// we want to own the routes for this interface
_, _ = sysctl.Sysctl(fmt.Sprintf("net/ipv6/conf/%s/accept_ra", hostVethName), "0")
return nil
})
if err != nil {
return net.Interface{}, net.Interface{}, err
}
return ifaceFromNetlinkLink(hostVeth), ifaceFromNetlinkLink(contVeth), nil
}
// SetupVeth sets up a pair of virtual ethernet devices.
// Call SetupVeth from inside the container netns. It will create both veth
// devices and move the host-side veth into the provided hostNS namespace.
// On success, SetupVeth returns (hostVeth, containerVeth, nil)
func SetupVeth(contVethName string, mtu int, hostNS ns.NetNS) (net.Interface, net.Interface, error) {
return SetupVethWithName(contVethName, "", mtu, hostNS)
}
// DelLinkByName removes an interface link.
func DelLinkByName(ifName string) error {
iface, err := netlink.LinkByName(ifName)
if err != nil {
if _, ok := err.(netlink.LinkNotFoundError); ok {
return ErrLinkNotFound
}
return fmt.Errorf("failed to lookup %q: %v", ifName, err)
}
if err = netlink.LinkDel(iface); err != nil {
return fmt.Errorf("failed to delete %q: %v", ifName, err)
}
return nil
}
// DelLinkByNameAddr remove an interface and returns its addresses
func DelLinkByNameAddr(ifName string) ([]*net.IPNet, error) {
iface, err := netlink.LinkByName(ifName)
if err != nil {
if _, ok := err.(netlink.LinkNotFoundError); ok {
return nil, ErrLinkNotFound
}
return nil, fmt.Errorf("failed to lookup %q: %v", ifName, err)
}
addrs, err := netlink.AddrList(iface, netlink.FAMILY_ALL)
if err != nil {
return nil, fmt.Errorf("failed to get IP addresses for %q: %v", ifName, err)
}
if err = netlink.LinkDel(iface); err != nil {
return nil, fmt.Errorf("failed to delete %q: %v", ifName, err)
}
out := []*net.IPNet{}
for _, addr := range addrs {
if addr.IP.IsGlobalUnicast() {
out = append(out, addr.IPNet)
}
}
return out, nil
}
func SetHWAddrByIP(ifName string, ip4 net.IP, ip6 net.IP) error {
iface, err := netlink.LinkByName(ifName)
if err != nil {
return fmt.Errorf("failed to lookup %q: %v", ifName, err)
}
switch {
case ip4 == nil && ip6 == nil:
return fmt.Errorf("neither ip4 or ip6 specified")
case ip4 != nil:
{
hwAddr, err := hwaddr.GenerateHardwareAddr4(ip4, hwaddr.PrivateMACPrefix)
if err != nil {
return fmt.Errorf("failed to generate hardware addr: %v", err)
}
if err = netlink.LinkSetHardwareAddr(iface, hwAddr); err != nil {
return fmt.Errorf("failed to add hardware addr to %q: %v", ifName, err)
}
}
case ip6 != nil:
// TODO: IPv6
}
return nil
}
// GetVethPeerIfindex returns the veth link object, the peer ifindex of the
// veth, or an error. This peer ifindex will only be valid in the peer's
// network namespace.
func GetVethPeerIfindex(ifName string) (netlink.Link, int, error) {
link, err := netlink.LinkByName(ifName)
if err != nil {
return nil, -1, fmt.Errorf("could not look up %q: %v", ifName, err)
}
if _, ok := link.(*netlink.Veth); !ok {
return nil, -1, fmt.Errorf("interface %q was not a veth interface", ifName)
}
// veth supports IFLA_LINK (what vishvananda/netlink calls ParentIndex)
// on 4.1 and higher kernels
peerIndex := link.Attrs().ParentIndex
if peerIndex <= 0 {
// Fall back to ethtool for 4.0 and earlier kernels
e, err := ethtool.NewEthtool()
if err != nil {
return nil, -1, fmt.Errorf("failed to initialize ethtool: %v", err)
}
defer e.Close()
stats, err := e.Stats(link.Attrs().Name)
if err != nil {
return nil, -1, fmt.Errorf("failed to request ethtool stats: %v", err)
}
n, ok := stats["peer_ifindex"]
if !ok {
return nil, -1, fmt.Errorf("failed to find 'peer_ifindex' in ethtool stats")
}
if n > 32767 || n == 0 {
return nil, -1, fmt.Errorf("invalid 'peer_ifindex' %d", n)
}
peerIndex = int(n)
}
return link, peerIndex, nil
}

310
pkg/ip/link_linux_test.go Normal file
View File

@ -0,0 +1,310 @@
// Copyright 2016 CNI authors
//
// 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.
package ip_test
import (
"bytes"
"crypto/rand"
"fmt"
"net"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/containernetworking/plugins/pkg/ip"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
"github.com/vishvananda/netlink"
)
func getHwAddr(linkname string) string {
veth, err := netlink.LinkByName(linkname)
Expect(err).NotTo(HaveOccurred())
return fmt.Sprintf("%s", veth.Attrs().HardwareAddr)
}
var _ = Describe("Link", func() {
const (
ifaceFormatString string = "i%d"
mtu int = 1400
ip4onehwaddr = "0a:58:01:01:01:01"
)
var (
hostNetNS ns.NetNS
containerNetNS ns.NetNS
ifaceCounter int = 0
hostVeth net.Interface
containerVeth net.Interface
hostVethName string
containerVethName string
ip4one = net.ParseIP("1.1.1.1")
ip4two = net.ParseIP("1.1.1.2")
originalRandReader = rand.Reader
)
BeforeEach(func() {
var err error
hostNetNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
containerNetNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
fakeBytes := make([]byte, 20)
//to be reset in AfterEach block
rand.Reader = bytes.NewReader(fakeBytes)
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
hostVeth, containerVeth, err = ip.SetupVeth(fmt.Sprintf(ifaceFormatString, ifaceCounter), mtu, hostNetNS)
if err != nil {
return err
}
Expect(err).NotTo(HaveOccurred())
hostVethName = hostVeth.Name
containerVethName = containerVeth.Name
return nil
})
})
AfterEach(func() {
Expect(containerNetNS.Close()).To(Succeed())
Expect(hostNetNS.Close()).To(Succeed())
ifaceCounter++
rand.Reader = originalRandReader
})
Describe("GetVethPeerIfindex", func() {
It("returns the link and peer index of the named interface", func() {
By("looking up the container veth index using the host veth name")
_ = hostNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
gotHostLink, gotContainerIndex, err := ip.GetVethPeerIfindex(hostVethName)
Expect(err).NotTo(HaveOccurred())
By("checking we got back the host link")
attrs := gotHostLink.Attrs()
Expect(attrs.Index).To(Equal(hostVeth.Index))
Expect(attrs.Name).To(Equal(hostVeth.Name))
By("checking we got back the container veth index")
Expect(gotContainerIndex).To(Equal(containerVeth.Index))
return nil
})
By("looking up the host veth index using the container veth name")
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
gotContainerLink, gotHostIndex, err := ip.GetVethPeerIfindex(containerVethName)
Expect(err).NotTo(HaveOccurred())
By("checking we got back the container link")
attrs := gotContainerLink.Attrs()
Expect(attrs.Index).To(Equal(containerVeth.Index))
Expect(attrs.Name).To(Equal(containerVeth.Name))
By("checking we got back the host veth index")
Expect(gotHostIndex).To(Equal(hostVeth.Index))
return nil
})
})
})
It("SetupVeth must put the veth endpoints into the separate namespaces", func() {
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
containerVethFromName, err := netlink.LinkByName(containerVethName)
Expect(err).NotTo(HaveOccurred())
Expect(containerVethFromName.Attrs().Index).To(Equal(containerVeth.Index))
return nil
})
_ = hostNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
hostVethFromName, err := netlink.LinkByName(hostVethName)
Expect(err).NotTo(HaveOccurred())
Expect(hostVethFromName.Attrs().Index).To(Equal(hostVeth.Index))
return nil
})
})
Context("when container already has an interface with the same name", func() {
It("returns useful error", func() {
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
_, _, err := ip.SetupVeth(containerVethName, mtu, hostNetNS)
Expect(err.Error()).To(Equal(fmt.Sprintf("container veth name provided (%s) already exists", containerVethName)))
return nil
})
})
})
Context("deleting an non-existent device", func() {
It("returns known error", func() {
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
// This string should match the expected error codes in the cmdDel functions of some of the plugins
_, err := ip.DelLinkByNameAddr("THIS_DONT_EXIST")
Expect(err).To(Equal(ip.ErrLinkNotFound))
return nil
})
})
})
Context("when there is no name available for the host-side", func() {
BeforeEach(func() {
//adding different interface to container ns
containerVethName += "0"
})
It("returns useful error", func() {
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
_, _, err := ip.SetupVeth(containerVethName, mtu, hostNetNS)
Expect(err.Error()).To(HavePrefix("failed to move veth to host netns: "))
return nil
})
})
})
Context("when there is no name conflict for the host or container interfaces", func() {
BeforeEach(func() {
//adding different interface to container and host ns
containerVethName += "0"
rand.Reader = originalRandReader
})
It("successfully creates the second veth pair", func() {
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
hostVeth, _, err := ip.SetupVeth(containerVethName, mtu, hostNetNS)
Expect(err).NotTo(HaveOccurred())
hostVethName = hostVeth.Name
return nil
})
//verify veths are in different namespaces
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
_, err := netlink.LinkByName(containerVethName)
Expect(err).NotTo(HaveOccurred())
return nil
})
_ = hostNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
_, err := netlink.LinkByName(hostVethName)
Expect(err).NotTo(HaveOccurred())
return nil
})
})
})
It("DelLinkByName must delete the veth endpoints", func() {
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
// this will delete the host endpoint too
err := ip.DelLinkByName(containerVethName)
Expect(err).NotTo(HaveOccurred())
_, err = netlink.LinkByName(containerVethName)
Expect(err).To(HaveOccurred())
return nil
})
_ = hostNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
_, err := netlink.LinkByName(hostVethName)
Expect(err).To(HaveOccurred())
return nil
})
})
It("DelLinkByNameAddr should return no IPs when no IPs are configured", func() {
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
// this will delete the host endpoint too
addr, err := ip.DelLinkByNameAddr(containerVethName)
Expect(err).NotTo(HaveOccurred())
Expect(addr).To(HaveLen(0))
return nil
})
})
It("SetHWAddrByIP must change the interface hwaddr and be predictable", func() {
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
var err error
hwaddrBefore := getHwAddr(containerVethName)
err = ip.SetHWAddrByIP(containerVethName, ip4one, nil)
Expect(err).NotTo(HaveOccurred())
hwaddrAfter1 := getHwAddr(containerVethName)
Expect(hwaddrBefore).NotTo(Equal(hwaddrAfter1))
Expect(hwaddrAfter1).To(Equal(ip4onehwaddr))
return nil
})
})
It("SetHWAddrByIP must be injective", func() {
_ = containerNetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
err := ip.SetHWAddrByIP(containerVethName, ip4one, nil)
Expect(err).NotTo(HaveOccurred())
hwaddrAfter1 := getHwAddr(containerVethName)
err = ip.SetHWAddrByIP(containerVethName, ip4two, nil)
Expect(err).NotTo(HaveOccurred())
hwaddrAfter2 := getHwAddr(containerVethName)
Expect(hwaddrAfter1).NotTo(Equal(hwaddrAfter2))
return nil
})
})
})

View File

@ -1,4 +1,4 @@
// Copyright 2015 CNI authors
// Copyright 2015-2017 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -20,12 +20,6 @@ import (
"github.com/vishvananda/netlink"
)
// AddDefaultRoute sets the default route on the given gateway.
func AddDefaultRoute(gw net.IP, dev netlink.Link) error {
_, defNet, _ := net.ParseCIDR("0.0.0.0/0")
return AddRoute(defNet, gw, dev)
}
// AddRoute adds a universally-scoped route to a device.
func AddRoute(ipn *net.IPNet, gw net.IP, dev netlink.Link) error {
return netlink.RouteAdd(&netlink.Route{
@ -45,3 +39,9 @@ func AddHostRoute(ipn *net.IPNet, gw net.IP, dev netlink.Link) error {
Gw: gw,
})
}
// AddDefaultRoute sets the default route on the given gateway.
func AddDefaultRoute(gw net.IP, dev netlink.Link) error {
_, defNet, _ := net.ParseCIDR("0.0.0.0/0")
return AddRoute(defNet, gw, dev)
}

120
pkg/ip/utils_linux.go Normal file
View File

@ -0,0 +1,120 @@
// +build linux
// Copyright 2016 CNI authors
//
// 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.
package ip
import (
"fmt"
"net"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/vishvananda/netlink"
)
func ValidateExpectedInterfaceIPs(ifName string, resultIPs []*current.IPConfig) error {
// Ensure ips
for _, ips := range resultIPs {
ourAddr := netlink.Addr{IPNet: &ips.Address}
match := false
link, err := netlink.LinkByName(ifName)
if err != nil {
return fmt.Errorf("Cannot find container link %v", ifName)
}
addrList, err := netlink.AddrList(link, netlink.FAMILY_ALL)
if err != nil {
return fmt.Errorf("Cannot obtain List of IP Addresses")
}
for _, addr := range addrList {
if addr.Equal(ourAddr) {
match = true
break
}
}
if match == false {
return fmt.Errorf("Failed to match addr %v on interface %v", ourAddr, ifName)
}
// Convert the host/prefixlen to just prefix for route lookup.
_, ourPrefix, err := net.ParseCIDR(ourAddr.String())
findGwy := &netlink.Route{Dst: ourPrefix}
routeFilter := netlink.RT_FILTER_DST
var family int
switch {
case ips.Version == "4":
family = netlink.FAMILY_V4
case ips.Version == "6":
family = netlink.FAMILY_V6
default:
return fmt.Errorf("Invalid IP Version %v for interface %v", ips.Version, ifName)
}
gwy, err := netlink.RouteListFiltered(family, findGwy, routeFilter)
if err != nil {
return fmt.Errorf("Error %v trying to find Gateway %v for interface %v", err, ips.Gateway, ifName)
}
if gwy == nil {
return fmt.Errorf("Failed to find Gateway %v for interface %v", ips.Gateway, ifName)
}
}
return nil
}
func ValidateExpectedRoute(resultRoutes []*types.Route) error {
// Ensure that each static route in prevResults is found in the routing table
for _, route := range resultRoutes {
find := &netlink.Route{Dst: &route.Dst, Gw: route.GW}
routeFilter := netlink.RT_FILTER_DST | netlink.RT_FILTER_GW
var family int
switch {
case route.Dst.IP.To4() != nil:
family = netlink.FAMILY_V4
// Default route needs Dst set to nil
if route.Dst.String() == "0.0.0.0/0" {
find = &netlink.Route{Dst: nil, Gw: route.GW}
routeFilter = netlink.RT_FILTER_DST
}
case len(route.Dst.IP) == net.IPv6len:
family = netlink.FAMILY_V6
// Default route needs Dst set to nil
if route.Dst.String() == "::/0" {
find = &netlink.Route{Dst: nil, Gw: route.GW}
routeFilter = netlink.RT_FILTER_DST
}
default:
return fmt.Errorf("Invalid static route found %v", route)
}
wasFound, err := netlink.RouteListFiltered(family, find, routeFilter)
if err != nil {
return fmt.Errorf("Expected Route %v not route table lookup error %v", route, err)
}
if wasFound == nil {
return fmt.Errorf("Expected Route %v not found in routing table", route)
}
}
return nil
}

View File

@ -15,54 +15,19 @@
package ipam
import (
"fmt"
"os"
"context"
"github.com/containernetworking/cni/pkg/invoke"
"github.com/containernetworking/cni/pkg/ip"
"github.com/containernetworking/cni/pkg/types"
"github.com/vishvananda/netlink"
)
func ExecAdd(plugin string, netconf []byte) (*types.Result, error) {
return invoke.DelegateAdd(plugin, netconf)
func ExecAdd(plugin string, netconf []byte) (types.Result, error) {
return invoke.DelegateAdd(context.TODO(), plugin, netconf, nil)
}
func ExecCheck(plugin string, netconf []byte) error {
return invoke.DelegateCheck(context.TODO(), plugin, netconf, nil)
}
func ExecDel(plugin string, netconf []byte) error {
return invoke.DelegateDel(plugin, netconf)
}
// ConfigureIface takes the result of IPAM plugin and
// applies to the ifName interface
func ConfigureIface(ifName string, res *types.Result) error {
link, err := netlink.LinkByName(ifName)
if err != nil {
return fmt.Errorf("failed to lookup %q: %v", ifName, err)
}
if err := netlink.LinkSetUp(link); err != nil {
return fmt.Errorf("failed to set %q UP: %v", ifName, err)
}
// TODO(eyakubovich): IPv6
addr := &netlink.Addr{IPNet: &res.IP4.IP, Label: ""}
if err = netlink.AddrAdd(link, addr); err != nil {
return fmt.Errorf("failed to add IP addr to %q: %v", ifName, err)
}
for _, r := range res.IP4.Routes {
gw := r.GW
if gw == nil {
gw = res.IP4.Gateway
}
if err = ip.AddRoute(&r.Dst, gw, link); err != nil {
// we skip over duplicate routes as we assume the first one wins
if !os.IsExist(err) {
return fmt.Errorf("failed to add route '%v via %v dev %v': %v", r.Dst, gw, ifName, err)
}
}
}
return nil
return invoke.DelegateDel(context.TODO(), plugin, netconf, nil)
}

121
pkg/ipam/ipam_linux.go Normal file
View File

@ -0,0 +1,121 @@
// Copyright 2015 CNI authors
//
// 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.
package ipam
import (
"fmt"
"net"
"os"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/plugins/pkg/ip"
"github.com/containernetworking/plugins/pkg/utils/sysctl"
"github.com/vishvananda/netlink"
)
const (
DisableIPv6SysctlTemplate = "net.ipv6.conf.%s.disable_ipv6"
)
// ConfigureIface takes the result of IPAM plugin and
// applies to the ifName interface
func ConfigureIface(ifName string, res *current.Result) error {
if len(res.Interfaces) == 0 {
return fmt.Errorf("no interfaces to configure")
}
link, err := netlink.LinkByName(ifName)
if err != nil {
return fmt.Errorf("failed to lookup %q: %v", ifName, err)
}
if err := netlink.LinkSetUp(link); err != nil {
return fmt.Errorf("failed to set %q UP: %v", ifName, err)
}
var v4gw, v6gw net.IP
var has_enabled_ipv6 bool = false
for _, ipc := range res.IPs {
if ipc.Interface == nil {
continue
}
intIdx := *ipc.Interface
if intIdx < 0 || intIdx >= len(res.Interfaces) || res.Interfaces[intIdx].Name != ifName {
// IP address is for a different interface
return fmt.Errorf("failed to add IP addr %v to %q: invalid interface index", ipc, ifName)
}
// Make sure sysctl "disable_ipv6" is 0 if we are about to add
// an IPv6 address to the interface
if !has_enabled_ipv6 && ipc.Version == "6" {
// Enabled IPv6 for loopback "lo" and the interface
// being configured
for _, iface := range [2]string{"lo", ifName} {
ipv6SysctlValueName := fmt.Sprintf(DisableIPv6SysctlTemplate, iface)
// Read current sysctl value
value, err := sysctl.Sysctl(ipv6SysctlValueName)
if err != nil || value == "0" {
// FIXME: log warning if unable to read sysctl value
continue
}
// Write sysctl to enable IPv6
_, err = sysctl.Sysctl(ipv6SysctlValueName, "0")
if err != nil {
return fmt.Errorf("failed to enable IPv6 for interface %q (%s=%s): %v", iface, ipv6SysctlValueName, value, err)
}
}
has_enabled_ipv6 = true
}
addr := &netlink.Addr{IPNet: &ipc.Address, Label: ""}
if err = netlink.AddrAdd(link, addr); err != nil {
return fmt.Errorf("failed to add IP addr %v to %q: %v", ipc, ifName, err)
}
gwIsV4 := ipc.Gateway.To4() != nil
if gwIsV4 && v4gw == nil {
v4gw = ipc.Gateway
} else if !gwIsV4 && v6gw == nil {
v6gw = ipc.Gateway
}
}
if v6gw != nil {
ip.SettleAddresses(ifName, 10)
}
for _, r := range res.Routes {
routeIsV4 := r.Dst.IP.To4() != nil
gw := r.GW
if gw == nil {
if routeIsV4 && v4gw != nil {
gw = v4gw
} else if !routeIsV4 && v6gw != nil {
gw = v6gw
}
}
if err = ip.AddRoute(&r.Dst, gw, link); err != nil {
// we skip over duplicate routes as we assume the first one wins
if !os.IsExist(err) {
return fmt.Errorf("failed to add route '%v via %v dev %v': %v", r.Dst, gw, ifName, err)
}
}
}
return nil
}

300
pkg/ipam/ipam_linux_test.go Normal file
View File

@ -0,0 +1,300 @@
// Copyright 2015 CNI authors
//
// 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.
package ipam
import (
"net"
"syscall"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
"github.com/vishvananda/netlink"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
const LINK_NAME = "eth0"
func ipNetEqual(a, b *net.IPNet) bool {
aPrefix, aBits := a.Mask.Size()
bPrefix, bBits := b.Mask.Size()
if aPrefix != bPrefix || aBits != bBits {
return false
}
return a.IP.Equal(b.IP)
}
var _ = Describe("ConfigureIface", func() {
var originalNS ns.NetNS
var ipv4, ipv6, routev4, routev6 *net.IPNet
var ipgw4, ipgw6, routegwv4, routegwv6 net.IP
var result *current.Result
BeforeEach(func() {
// Create a new NetNS so we don't modify the host
var err error
originalNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
// Add master
err = netlink.LinkAdd(&netlink.Dummy{
LinkAttrs: netlink.LinkAttrs{
Name: LINK_NAME,
},
})
Expect(err).NotTo(HaveOccurred())
_, err = netlink.LinkByName(LINK_NAME)
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
ipv4, err = types.ParseCIDR("1.2.3.30/24")
Expect(err).NotTo(HaveOccurred())
Expect(ipv4).NotTo(BeNil())
_, routev4, err = net.ParseCIDR("15.5.6.8/24")
Expect(err).NotTo(HaveOccurred())
Expect(routev4).NotTo(BeNil())
routegwv4 = net.ParseIP("1.2.3.5")
Expect(routegwv4).NotTo(BeNil())
ipgw4 = net.ParseIP("1.2.3.1")
Expect(ipgw4).NotTo(BeNil())
ipv6, err = types.ParseCIDR("abcd:1234:ffff::cdde/64")
Expect(err).NotTo(HaveOccurred())
Expect(ipv6).NotTo(BeNil())
_, routev6, err = net.ParseCIDR("1111:dddd::aaaa/80")
Expect(err).NotTo(HaveOccurred())
Expect(routev6).NotTo(BeNil())
routegwv6 = net.ParseIP("abcd:1234:ffff::10")
Expect(routegwv6).NotTo(BeNil())
ipgw6 = net.ParseIP("abcd:1234:ffff::1")
Expect(ipgw6).NotTo(BeNil())
result = &current.Result{
Interfaces: []*current.Interface{
{
Name: "eth0",
Mac: "00:11:22:33:44:55",
Sandbox: "/proc/3553/ns/net",
},
{
Name: "fake0",
Mac: "00:33:44:55:66:77",
Sandbox: "/proc/1234/ns/net",
},
},
IPs: []*current.IPConfig{
{
Version: "4",
Interface: current.Int(0),
Address: *ipv4,
Gateway: ipgw4,
},
{
Version: "6",
Interface: current.Int(0),
Address: *ipv6,
Gateway: ipgw6,
},
},
Routes: []*types.Route{
{Dst: *routev4, GW: routegwv4},
{Dst: *routev6, GW: routegwv6},
},
}
})
AfterEach(func() {
Expect(originalNS.Close()).To(Succeed())
})
It("configures a link with addresses and routes", func() {
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
err := ConfigureIface(LINK_NAME, result)
Expect(err).NotTo(HaveOccurred())
link, err := netlink.LinkByName(LINK_NAME)
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal(LINK_NAME))
v4addrs, err := netlink.AddrList(link, syscall.AF_INET)
Expect(err).NotTo(HaveOccurred())
Expect(len(v4addrs)).To(Equal(1))
Expect(ipNetEqual(v4addrs[0].IPNet, ipv4)).To(Equal(true))
v6addrs, err := netlink.AddrList(link, syscall.AF_INET6)
Expect(err).NotTo(HaveOccurred())
Expect(len(v6addrs)).To(Equal(2))
var found bool
for _, a := range v6addrs {
if ipNetEqual(a.IPNet, ipv6) {
found = true
break
}
}
Expect(found).To(Equal(true))
// Ensure the v4 route, v6 route, and subnet route
routes, err := netlink.RouteList(link, 0)
Expect(err).NotTo(HaveOccurred())
var v4found, v6found bool
for _, route := range routes {
isv4 := route.Dst.IP.To4() != nil
if isv4 && ipNetEqual(route.Dst, routev4) && route.Gw.Equal(routegwv4) {
v4found = true
}
if !isv4 && ipNetEqual(route.Dst, routev6) && route.Gw.Equal(routegwv6) {
v6found = true
}
if v4found && v6found {
break
}
}
Expect(v4found).To(Equal(true))
Expect(v6found).To(Equal(true))
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It("configures a link with routes using address gateways", func() {
result.Routes[0].GW = nil
result.Routes[1].GW = nil
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
err := ConfigureIface(LINK_NAME, result)
Expect(err).NotTo(HaveOccurred())
link, err := netlink.LinkByName(LINK_NAME)
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal(LINK_NAME))
// Ensure the v4 route, v6 route, and subnet route
routes, err := netlink.RouteList(link, 0)
Expect(err).NotTo(HaveOccurred())
var v4found, v6found bool
for _, route := range routes {
isv4 := route.Dst.IP.To4() != nil
if isv4 && ipNetEqual(route.Dst, routev4) && route.Gw.Equal(ipgw4) {
v4found = true
}
if !isv4 && ipNetEqual(route.Dst, routev6) && route.Gw.Equal(ipgw6) {
v6found = true
}
if v4found && v6found {
break
}
}
Expect(v4found).To(Equal(true))
Expect(v6found).To(Equal(true))
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It("returns an error when the interface index doesn't match the link name", func() {
result.IPs[0].Interface = current.Int(1)
err := originalNS.Do(func(ns.NetNS) error {
return ConfigureIface(LINK_NAME, result)
})
Expect(err).To(HaveOccurred())
})
It("returns an error when the interface index is too big", func() {
result.IPs[0].Interface = current.Int(2)
err := originalNS.Do(func(ns.NetNS) error {
return ConfigureIface(LINK_NAME, result)
})
Expect(err).To(HaveOccurred())
})
It("returns an error when the interface index is too small", func() {
result.IPs[0].Interface = current.Int(-1)
err := originalNS.Do(func(ns.NetNS) error {
return ConfigureIface(LINK_NAME, result)
})
Expect(err).To(HaveOccurred())
})
It("returns an error when there are no interfaces to configure", func() {
result.Interfaces = []*current.Interface{}
err := originalNS.Do(func(ns.NetNS) error {
return ConfigureIface(LINK_NAME, result)
})
Expect(err).To(HaveOccurred())
})
It("returns an error when configuring the wrong interface", func() {
err := originalNS.Do(func(ns.NetNS) error {
return ConfigureIface("asdfasdf", result)
})
Expect(err).To(HaveOccurred())
})
It("does not panic when interface is not specified", func() {
result = &current.Result{
Interfaces: []*current.Interface{
{
Name: "eth0",
Mac: "00:11:22:33:44:55",
Sandbox: "/proc/3553/ns/net",
},
{
Name: "fake0",
Mac: "00:33:44:55:66:77",
Sandbox: "/proc/1234/ns/net",
},
},
IPs: []*current.IPConfig{
{
Version: "4",
Address: *ipv4,
Gateway: ipgw4,
},
{
Version: "6",
Address: *ipv6,
Gateway: ipgw6,
},
},
}
err := originalNS.Do(func(ns.NetNS) error {
return ConfigureIface(LINK_NAME, result)
})
Expect(err).NotTo(HaveOccurred())
})
})

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package types_test
package ipam_test
import (
. "github.com/onsi/ginkgo"
@ -21,7 +21,7 @@ import (
"testing"
)
func TestTypes(t *testing.T) {
func TestIpam(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Types Suite")
RunSpecs(t, "pkg/ipam")
}

View File

@ -9,13 +9,9 @@ Go provides the `runtime.LockOSThread()` function to ensure a specific goroutine
For example, you cannot rely on the `ns.Set()` namespace being the current namespace after the `Set()` call unless you do two things. First, the goroutine calling `Set()` must have previously called `LockOSThread()`. Second, you must ensure `runtime.UnlockOSThread()` is not called somewhere in-between. You also cannot rely on the initial network namespace remaining the current network namespace if any other code in your program switches namespaces, unless you have already called `LockOSThread()` in that goroutine. Note that `LockOSThread()` prevents the Go scheduler from optimally scheduling goroutines for best performance, so `LockOSThread()` should only be used in small, isolated goroutines that release the lock quickly.
### Do() The Recommended Thing
The `ns.Do()` method provides control over network namespaces for you by implementing these strategies. All code dependent on a particular network namespace should be wrapped in the `ns.Do()` method to ensure the correct namespace is selected for the duration of your code. For example:
The `ns.Do()` method provides **partial** control over network namespaces for you by implementing these strategies. All code dependent on a particular network namespace (including the root namespace) should be wrapped in the `ns.Do()` method to ensure the correct namespace is selected for the duration of your code. For example:
```go
targetNs, err := ns.NewNS()
if err != nil {
return err
}
err = targetNs.Do(func(hostNs ns.NetNS) error {
dummy := &netlink.Dummy{
LinkAttrs: netlink.LinkAttrs{
@ -26,6 +22,20 @@ err = targetNs.Do(func(hostNs ns.NetNS) error {
})
```
Note this requirement to wrap every network call is very onerous - any libraries you call might call out to network services such as DNS, and all such calls need to be protected after you call `ns.Do()`. All goroutines spawned from within the `ns.Do` will not inherit the new namespace. The CNI plugins all exit very soon after calling `ns.Do()` which helps to minimize the problem.
When a new thread is spawned in Linux, it inherits the namespace of its parent. In versions of go **prior to 1.10**, if the runtime spawns a new OS thread, it picks the parent randomly. If the chosen parent thread has been moved to a new namespace (even temporarily), the new OS thread will be permanently "stuck in the wrong namespace", and goroutines will non-deterministically switch namespaces as they are rescheduled.
In short, **there was no safe way to change network namespaces, even temporarily, from within a long-lived, multithreaded Go process**. If you wish to do this, you must use go 1.10 or greater.
### Creating network namespaces
Earlier versions of this library managed namespace creation, but as CNI does not actually utilize this feature (and it was essentially unmaintained), it was removed. If you're writing a container runtime, you should implement namespace management yourself. However, there are some gotchas when doing so, especially around handling `/var/run/netns`. A reasonably correct reference implementation, borrowed from `rkt`, can be found in `pkg/testutils/netns_linux.go` if you're in need of a source of inspiration.
### Further Reading
- https://github.com/golang/go/wiki/LockOSThread
- http://morsmachine.dk/go-scheduler
- https://github.com/containernetworking/cni/issues/262
- https://golang.org/pkg/runtime/
- https://www.weave.works/blog/linux-namespaces-and-go-don-t-mix

View File

@ -1,4 +1,4 @@
// Copyright 2015 CNI authors
// Copyright 2015-2017 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -15,21 +15,55 @@
package ns
import (
"crypto/rand"
"fmt"
"os"
"path"
"runtime"
"strings"
"sync"
"syscall"
"golang.org/x/sys/unix"
)
// Returns an object representing the current OS thread's network namespace
func GetCurrentNS() (NetNS, error) {
return GetNS(getCurrentThreadNetNSPath())
}
func getCurrentThreadNetNSPath() string {
// /proc/self/ns/net returns the namespace of the main thread, not
// of whatever thread this goroutine is running on. Make sure we
// use the thread's net namespace since the thread is switching around
return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid())
}
func (ns *netNS) Close() error {
if err := ns.errorIfClosed(); err != nil {
return err
}
if err := ns.file.Close(); err != nil {
return fmt.Errorf("Failed to close %q: %v", ns.file.Name(), err)
}
ns.closed = true
return nil
}
func (ns *netNS) Set() error {
if err := ns.errorIfClosed(); err != nil {
return err
}
if err := unix.Setns(int(ns.Fd()), unix.CLONE_NEWNET); err != nil {
return fmt.Errorf("Error switching to ns %v: %v", ns.file.Name(), err)
}
return nil
}
type NetNS interface {
// Executes the passed closure in this object's network namespace,
// attemtping to restore the original namespace before returning.
// attempting to restore the original namespace before returning.
// However, since each OS thread can have a different network namespace,
// and Go's thread scheduling is highly variable, callers cannot
// guarantee any specific namespace is set unless operations that
@ -58,22 +92,12 @@ type NetNS interface {
}
type netNS struct {
file *os.File
mounted bool
closed bool
file *os.File
closed bool
}
func getCurrentThreadNetNSPath() string {
// /proc/self/ns/net returns the namespace of the main thread, not
// of whatever thread this goroutine is running on. Make sure we
// use the thread's net namespace since the thread is switching around
return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid())
}
// Returns an object representing the current OS thread's network namespace
func GetCurrentNS() (NetNS, error) {
return GetNS(getCurrentThreadNetNSPath())
}
// netNS implements the NetNS interface
var _ NetNS = &netNS{}
const (
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/magic.h
@ -101,19 +125,7 @@ func IsNSorErr(nspath string) error {
}
switch stat.Type {
case PROCFS_MAGIC:
// Kernel < 3.19
validPathContent := "ns/"
validName := strings.Contains(nspath, validPathContent)
if !validName {
return NSPathNotNSErr{msg: fmt.Sprintf("path %q doesn't contain %q", nspath, validPathContent)}
}
return nil
case NSFS_MAGIC:
// Kernel >= 3.19
case PROCFS_MAGIC, NSFS_MAGIC:
return nil
default:
return NSPathNotNSErr{msg: fmt.Sprintf("unknown FS magic on %q: %x", nspath, stat.Type)}
@ -135,82 +147,6 @@ func GetNS(nspath string) (NetNS, error) {
return &netNS{file: fd}, nil
}
// Creates a new persistent network namespace and returns an object
// representing that namespace, without switching to it
func NewNS() (NetNS, error) {
const nsRunDir = "/var/run/netns"
b := make([]byte, 16)
_, err := rand.Reader.Read(b)
if err != nil {
return nil, fmt.Errorf("failed to generate random netns name: %v", err)
}
err = os.MkdirAll(nsRunDir, 0755)
if err != nil {
return nil, err
}
// create an empty file at the mount point
nsName := fmt.Sprintf("cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
nsPath := path.Join(nsRunDir, nsName)
mountPointFd, err := os.Create(nsPath)
if err != nil {
return nil, err
}
mountPointFd.Close()
// Ensure the mount point is cleaned up on errors; if the namespace
// was successfully mounted this will have no effect because the file
// is in-use
defer os.RemoveAll(nsPath)
var wg sync.WaitGroup
wg.Add(1)
// do namespace work in a dedicated goroutine, so that we can safely
// Lock/Unlock OSThread without upsetting the lock/unlock state of
// the caller of this function
var fd *os.File
go (func() {
defer wg.Done()
runtime.LockOSThread()
var origNS NetNS
origNS, err = GetNS(getCurrentThreadNetNSPath())
if err != nil {
return
}
defer origNS.Close()
// create a new netns on the current thread
err = unix.Unshare(unix.CLONE_NEWNET)
if err != nil {
return
}
defer origNS.Set()
// bind mount the new netns from the current thread onto the mount point
err = unix.Mount(getCurrentThreadNetNSPath(), nsPath, "none", unix.MS_BIND, "")
if err != nil {
return
}
fd, err = os.Open(nsPath)
if err != nil {
return
}
})()
wg.Wait()
if err != nil {
unix.Unmount(nsPath, unix.MNT_DETACH)
return nil, fmt.Errorf("failed to create namespace: %v", err)
}
return &netNS{file: fd, mounted: true}, nil
}
func (ns *netNS) Path() string {
return ns.file.Name()
}
@ -226,36 +162,13 @@ func (ns *netNS) errorIfClosed() error {
return nil
}
func (ns *netNS) Close() error {
if err := ns.errorIfClosed(); err != nil {
return err
}
if err := ns.file.Close(); err != nil {
return fmt.Errorf("Failed to close %q: %v", ns.file.Name(), err)
}
ns.closed = true
if ns.mounted {
if err := unix.Unmount(ns.file.Name(), unix.MNT_DETACH); err != nil {
return fmt.Errorf("Failed to unmount namespace %s: %v", ns.file.Name(), err)
}
if err := os.RemoveAll(ns.file.Name()); err != nil {
return fmt.Errorf("Failed to clean up namespace %s: %v", ns.file.Name(), err)
}
ns.mounted = false
}
return nil
}
func (ns *netNS) Do(toRun func(NetNS) error) error {
if err := ns.errorIfClosed(); err != nil {
return err
}
containedCall := func(hostNS NetNS) error {
threadNS, err := GetNS(getCurrentThreadNetNSPath())
threadNS, err := GetCurrentNS()
if err != nil {
return fmt.Errorf("failed to open current netns: %v", err)
}
@ -265,13 +178,22 @@ func (ns *netNS) Do(toRun func(NetNS) error) error {
if err = ns.Set(); err != nil {
return fmt.Errorf("error switching to ns %v: %v", ns.file.Name(), err)
}
defer threadNS.Set() // switch back
defer func() {
err := threadNS.Set() // switch back
if err == nil {
// Unlock the current thread only when we successfully switched back
// to the original namespace; otherwise leave the thread locked which
// will force the runtime to scrap the current thread, that is maybe
// not as optimal but at least always safe to do.
runtime.UnlockOSThread()
}
}()
return toRun(hostNS)
}
// save a handle to current network namespace
hostNS, err := GetNS(getCurrentThreadNetNSPath())
hostNS, err := GetCurrentNS()
if err != nil {
return fmt.Errorf("Failed to open current namespace: %v", err)
}
@ -280,6 +202,10 @@ func (ns *netNS) Do(toRun func(NetNS) error) error {
var wg sync.WaitGroup
wg.Add(1)
// Start the callback in a new green thread so that if we later fail
// to switch the namespace back to the original one, we can safely
// leave the thread locked to die without a risk of the current thread
// left lingering with incorrect namespace.
var innerError error
go func() {
defer wg.Done()
@ -291,18 +217,6 @@ func (ns *netNS) Do(toRun func(NetNS) error) error {
return innerError
}
func (ns *netNS) Set() error {
if err := ns.errorIfClosed(); err != nil {
return err
}
if _, _, err := unix.Syscall(unix.SYS_SETNS, ns.Fd(), uintptr(unix.CLONE_NEWNET), 0); err != 0 {
return fmt.Errorf("Error switching to ns %v: %v", ns.file.Name(), err)
}
return nil
}
// WithNetNSPath executes the passed closure under the given network
// namespace, restoring the original namespace afterwards.
func WithNetNSPath(nspath string, toRun func(NetNS) error) error {

View File

@ -1,4 +1,4 @@
// Copyright 2016 CNI authors
// Copyright 2016-2018 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -21,7 +21,8 @@ import (
"os"
"path/filepath"
"github.com/containernetworking/cni/pkg/ns"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"golang.org/x/sys/unix"
@ -65,16 +66,19 @@ var _ = Describe("Linux namespace operations", func() {
BeforeEach(func() {
var err error
originalNetNS, err = ns.NewNS()
originalNetNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
targetNetNS, err = ns.NewNS()
targetNetNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
})
AfterEach(func() {
Expect(targetNetNS.Close()).To(Succeed())
Expect(originalNetNS.Close()).To(Succeed())
targetNetNS.Close()
originalNetNS.Close()
Expect(testutils.UnmountNS(targetNetNS)).To(Succeed())
Expect(testutils.UnmountNS(originalNetNS)).To(Succeed())
})
It("executes the callback within the target network namespace", func() {
@ -108,6 +112,7 @@ var _ = Describe("Linux namespace operations", func() {
Expect(hostNSInode).To(Equal(origNSInode))
return nil
})
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
@ -155,13 +160,14 @@ var _ = Describe("Linux namespace operations", func() {
It("should not leak a closed netns onto any threads in the process", func() {
By("creating a new netns")
createdNetNS, err := ns.NewNS()
createdNetNS, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
By("discovering the inode of the created netns")
createdNetNSInode, err := getInodeNS(createdNetNS)
Expect(err).NotTo(HaveOccurred())
createdNetNS.Close()
Expect(testutils.UnmountNS(createdNetNS)).NotTo(HaveOccurred())
By("comparing against the netns inode of every thread in the process")
for _, netnsPath := range allNetNSInCurrentProcess() {
@ -188,7 +194,8 @@ var _ = Describe("Linux namespace operations", func() {
Describe("closing a network namespace", func() {
It("should prevent further operations", func() {
createdNetNS, err := ns.NewNS()
createdNetNS, err := testutils.NewNS()
defer testutils.UnmountNS(createdNetNS)
Expect(err).NotTo(HaveOccurred())
err = createdNetNS.Close()
@ -202,8 +209,9 @@ var _ = Describe("Linux namespace operations", func() {
})
It("should only work once", func() {
createdNetNS, err := ns.NewNS()
createdNetNS, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
defer testutils.UnmountNS(createdNetNS)
err = createdNetNS.Close()
Expect(err).NotTo(HaveOccurred())
@ -216,7 +224,9 @@ var _ = Describe("Linux namespace operations", func() {
Describe("IsNSorErr", func() {
It("should detect a namespace", func() {
createdNetNS, err := ns.NewNS()
createdNetNS, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
defer testutils.UnmountNS(createdNetNS)
err = ns.IsNSorErr(createdNetNS.Path())
Expect(err).NotTo(HaveOccurred())
})

View File

@ -30,5 +30,5 @@ func TestNs(t *testing.T) {
runtime.LockOSThread()
RegisterFailHandler(Fail)
RunSpecs(t, "pkg/ns Suite")
RunSpecs(t, "pkg/ns")
}

View File

@ -1,161 +0,0 @@
// Copyright 2014 CNI authors
//
// 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.
// Package skel provides skeleton code for a CNI plugin.
// In particular, it implements argument parsing and validation.
package skel
import (
"fmt"
"io/ioutil"
"log"
"os"
"github.com/containernetworking/cni/pkg/types"
)
// CmdArgs captures all the arguments passed in to the plugin
// via both env vars and stdin
type CmdArgs struct {
ContainerID string
Netns string
IfName string
Args string
Path string
StdinData []byte
}
type reqForCmdEntry map[string]bool
// PluginMain is the "main" for a plugin. It accepts
// two callback functions for add and del commands.
func PluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error) {
var cmd, contID, netns, ifName, args, path string
vars := []struct {
name string
val *string
reqForCmd reqForCmdEntry
}{
{
"CNI_COMMAND",
&cmd,
reqForCmdEntry{
"ADD": true,
"DEL": true,
},
},
{
"CNI_CONTAINERID",
&contID,
reqForCmdEntry{
"ADD": false,
"DEL": false,
},
},
{
"CNI_NETNS",
&netns,
reqForCmdEntry{
"ADD": true,
"DEL": false,
},
},
{
"CNI_IFNAME",
&ifName,
reqForCmdEntry{
"ADD": true,
"DEL": true,
},
},
{
"CNI_ARGS",
&args,
reqForCmdEntry{
"ADD": false,
"DEL": false,
},
},
{
"CNI_PATH",
&path,
reqForCmdEntry{
"ADD": true,
"DEL": true,
},
},
}
argsMissing := false
for _, v := range vars {
*v.val = os.Getenv(v.name)
if v.reqForCmd[cmd] && *v.val == "" {
log.Printf("%v env variable missing", v.name)
argsMissing = true
}
}
if argsMissing {
dieMsg("required env variables missing")
}
stdinData, err := ioutil.ReadAll(os.Stdin)
if err != nil {
dieMsg("error reading from stdin: %v", err)
}
cmdArgs := &CmdArgs{
ContainerID: contID,
Netns: netns,
IfName: ifName,
Args: args,
Path: path,
StdinData: stdinData,
}
switch cmd {
case "ADD":
err = cmdAdd(cmdArgs)
case "DEL":
err = cmdDel(cmdArgs)
default:
dieMsg("unknown CNI_COMMAND: %v", cmd)
}
if err != nil {
if e, ok := err.(*types.Error); ok {
// don't wrap Error in Error
dieErr(e)
}
dieMsg(err.Error())
}
}
func dieMsg(f string, args ...interface{}) {
e := &types.Error{
Code: 100,
Msg: fmt.Sprintf(f, args...),
}
dieErr(e)
}
func dieErr(e *types.Error) {
if err := e.Print(); err != nil {
log.Print("Error writing error JSON to stdout: ", err)
}
os.Exit(1)
}

View File

@ -1,84 +0,0 @@
// Copyright 2016 CNI authors
//
// 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.
package skel
import (
"os"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Skel", func() {
var (
fNoop = func(_ *CmdArgs) error { return nil }
// fErr = func(_ *CmdArgs) error { return errors.New("dummy") }
envVars = []struct {
name string
val string
}{
{"CNI_CONTAINERID", "dummy"},
{"CNI_NETNS", "dummy"},
{"CNI_IFNAME", "dummy"},
{"CNI_ARGS", "dummy"},
{"CNI_PATH", "dummy"},
}
)
It("Must be possible to set the env vars", func() {
for _, v := range envVars {
err := os.Setenv(v.name, v.val)
Expect(err).NotTo(HaveOccurred())
}
})
Context("When dummy environment variables are passed", func() {
It("should not fail with ADD and noop callback", func() {
err := os.Setenv("CNI_COMMAND", "ADD")
Expect(err).NotTo(HaveOccurred())
PluginMain(fNoop, nil)
})
// TODO: figure out howto mock printing and os.Exit()
// It("should fail with ADD and error callback", func() {
// err := os.Setenv("CNI_COMMAND", "ADD")
// Expect(err).NotTo(HaveOccurred())
// PluginMain(fErr, nil)
// })
It("should not fail with DEL and noop callback", func() {
err := os.Setenv("CNI_COMMAND", "DEL")
Expect(err).NotTo(HaveOccurred())
PluginMain(nil, fNoop)
})
// TODO: figure out howto mock printing and os.Exit()
// It("should fail with DEL and error callback", func() {
// err := os.Setenv("CNI_COMMAND", "DEL")
// Expect(err).NotTo(HaveOccurred())
// PluginMain(fErr, nil)
// })
It("should not fail with DEL and no NETNS and noop callback", func() {
err := os.Setenv("CNI_COMMAND", "DEL")
Expect(err).NotTo(HaveOccurred())
err = os.Unsetenv("CNI_NETNS")
Expect(err).NotTo(HaveOccurred())
PluginMain(nil, fNoop)
})
})
})

View File

@ -0,0 +1,33 @@
// Copyright 2016 CNI authors
//
// 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.
package testutils
import "errors"
// BadReader is an io.Reader which always errors
type BadReader struct {
Error error
}
func (r *BadReader) Read(buffer []byte) (int, error) {
if r.Error != nil {
return 0, r.Error
}
return 0, errors.New("banana")
}
func (r *BadReader) Close() error {
return nil
}

View File

@ -15,11 +15,12 @@
package testutils
import (
"encoding/json"
"io/ioutil"
"os"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/version"
)
func envCleanup() {
@ -27,51 +28,85 @@ func envCleanup() {
os.Unsetenv("CNI_PATH")
os.Unsetenv("CNI_NETNS")
os.Unsetenv("CNI_IFNAME")
os.Unsetenv("CNI_CONTAINERID")
}
func CmdAddWithResult(cniNetns, cniIfname string, f func() error) (*types.Result, error) {
func CmdAdd(cniNetns, cniContainerID, cniIfname string, conf []byte, f func() error) (types.Result, []byte, error) {
os.Setenv("CNI_COMMAND", "ADD")
os.Setenv("CNI_PATH", os.Getenv("PATH"))
os.Setenv("CNI_NETNS", cniNetns)
os.Setenv("CNI_IFNAME", cniIfname)
os.Setenv("CNI_CONTAINERID", cniContainerID)
defer envCleanup()
// Redirect stdout to capture plugin result
oldStdout := os.Stdout
r, w, err := os.Pipe()
if err != nil {
return nil, err
return nil, nil, err
}
os.Stdout = w
err = f()
w.Close()
if err != nil {
return nil, err
}
// parse the result
out, err := ioutil.ReadAll(r)
var out []byte
if err == nil {
out, err = ioutil.ReadAll(r)
}
os.Stdout = oldStdout
// Return errors after restoring stdout so Ginkgo will correctly
// emit verbose error information on stdout
if err != nil {
return nil, err
return nil, nil, err
}
result := types.Result{}
err = json.Unmarshal(out, &result)
// Plugin must return result in same version as specified in netconf
versionDecoder := &version.ConfigDecoder{}
confVersion, err := versionDecoder.Decode(conf)
if err != nil {
return nil, err
return nil, nil, err
}
return &result, nil
result, err := version.NewResult(confVersion, out)
if err != nil {
return nil, nil, err
}
return result, out, nil
}
func CmdDelWithResult(cniNetns, cniIfname string, f func() error) error {
os.Setenv("CNI_COMMAND", "DEL")
func CmdAddWithArgs(args *skel.CmdArgs, f func() error) (types.Result, []byte, error) {
return CmdAdd(args.Netns, args.ContainerID, args.IfName, args.StdinData, f)
}
func CmdCheck(cniNetns, cniContainerID, cniIfname string, conf []byte, f func() error) error {
os.Setenv("CNI_COMMAND", "CHECK")
os.Setenv("CNI_PATH", os.Getenv("PATH"))
os.Setenv("CNI_NETNS", cniNetns)
os.Setenv("CNI_IFNAME", cniIfname)
os.Setenv("CNI_CONTAINERID", cniContainerID)
defer envCleanup()
return f()
}
func CmdCheckWithArgs(args *skel.CmdArgs, f func() error) error {
return CmdCheck(args.Netns, args.ContainerID, args.IfName, args.StdinData, f)
}
func CmdDel(cniNetns, cniContainerID, cniIfname string, f func() error) error {
os.Setenv("CNI_COMMAND", "DEL")
os.Setenv("CNI_PATH", os.Getenv("PATH"))
os.Setenv("CNI_NETNS", cniNetns)
os.Setenv("CNI_IFNAME", cniIfname)
os.Setenv("CNI_CONTAINERID", cniContainerID)
defer envCleanup()
return f()
}
func CmdDelWithArgs(args *skel.CmdArgs, f func() error) error {
return CmdDel(args.Netns, args.ContainerID, args.IfName, f)
}

60
pkg/testutils/dns.go Normal file
View File

@ -0,0 +1,60 @@
// Copyright 2019 CNI authors
//
// 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.
package testutils
import (
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/containernetworking/cni/pkg/types"
)
// TmpResolvConf will create a temporary file and write the provided DNS settings to
// it in the resolv.conf format. It returns the path of the created temporary file or
// an error if any occurs while creating/writing the file. It is the caller's
// responsibility to remove the file.
func TmpResolvConf(dnsConf types.DNS) (string, error) {
f, err := ioutil.TempFile("", "cni_test_resolv.conf")
if err != nil {
return "", fmt.Errorf("failed to get temp file for CNI test resolv.conf: %v", err)
}
defer f.Close()
path := f.Name()
defer func() {
if err != nil {
os.RemoveAll(path)
}
}()
// see "man 5 resolv.conf" for the format of resolv.conf
var resolvConfLines []string
for _, nameserver := range dnsConf.Nameservers {
resolvConfLines = append(resolvConfLines, fmt.Sprintf("nameserver %s", nameserver))
}
resolvConfLines = append(resolvConfLines, fmt.Sprintf("domain %s", dnsConf.Domain))
resolvConfLines = append(resolvConfLines, fmt.Sprintf("search %s", strings.Join(dnsConf.Search, " ")))
resolvConfLines = append(resolvConfLines, fmt.Sprintf("options %s", strings.Join(dnsConf.Options, " ")))
resolvConf := strings.Join(resolvConfLines, "\n")
_, err = f.Write([]byte(resolvConf))
if err != nil {
return "", fmt.Errorf("failed to write temp resolv.conf for CNI test: %v", err)
}
return path, err
}

View File

@ -0,0 +1,74 @@
package main_test
import (
"fmt"
"io/ioutil"
"net"
"os/exec"
"strings"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gbytes"
"github.com/onsi/gomega/gexec"
)
var binaryPath string
var _ = SynchronizedBeforeSuite(func() []byte {
binaryPath, err := gexec.Build("github.com/containernetworking/plugins/pkg/testutils/echosvr")
Expect(err).NotTo(HaveOccurred())
return []byte(binaryPath)
}, func(data []byte) {
binaryPath = string(data)
})
var _ = SynchronizedAfterSuite(func() {}, func() {
gexec.CleanupBuildArtifacts()
})
var _ = Describe("Echosvr", func() {
var session *gexec.Session
BeforeEach(func() {
var err error
cmd := exec.Command(binaryPath)
session, err = gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
Expect(err).NotTo(HaveOccurred())
})
AfterEach(func() {
session.Kill().Wait()
})
It("starts and doesn't terminate immediately", func() {
Consistently(session).ShouldNot(gexec.Exit())
})
tryConnect := func() (net.Conn, error) {
programOutput := session.Out.Contents()
addr := strings.TrimSpace(string(programOutput))
conn, err := net.Dial("tcp", addr)
if err != nil {
return nil, err
}
return conn, err
}
It("prints its listening address to stdout", func() {
Eventually(session.Out).Should(gbytes.Say("\n"))
conn, err := tryConnect()
Expect(err).NotTo(HaveOccurred())
conn.Close()
})
It("will echo data back to us", func() {
Eventually(session.Out).Should(gbytes.Say("\n"))
conn, err := tryConnect()
Expect(err).NotTo(HaveOccurred())
defer conn.Close()
fmt.Fprintf(conn, "hello\n")
Expect(ioutil.ReadAll(conn)).To(Equal([]byte("hello")))
})
})

View File

@ -0,0 +1,13 @@
package main_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
)
func TestEchosvr(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "pkg/testutils/echosvr")
}

View File

@ -0,0 +1,57 @@
// Echosvr is a simple TCP echo server
//
// It prints its listen address on stdout
// 127.0.0.1:xxxxx
// A test should wait for this line, parse it
// and may then attempt to connect.
package main
import (
"bufio"
"fmt"
"io"
"net"
"os"
"strings"
"time"
)
func main() {
listener, err := net.Listen("tcp", ":")
if err != nil {
panic(err)
}
_, port, err := net.SplitHostPort(listener.Addr().String())
if err != nil {
panic(err)
}
fmt.Printf("127.0.0.1:%s\n", port)
for {
conn, err := listener.Accept()
if err != nil {
panic(err)
}
go handleConnection(conn)
}
}
func handleConnection(conn net.Conn) {
conn.SetReadDeadline(time.Now().Add(1 * time.Minute))
content, err := bufio.NewReader(conn).ReadString('\n')
if err != nil && err != io.EOF {
fmt.Fprint(os.Stderr, err.Error())
return
}
conn.SetWriteDeadline(time.Now().Add(1 * time.Minute))
if _, err = conn.Write([]byte(strings.TrimSuffix(content, "\n"))); err != nil {
fmt.Fprint(os.Stderr, err.Error())
return
}
if err = conn.Close(); err != nil {
fmt.Fprint(os.Stderr, err.Error())
return
}
}

View File

@ -0,0 +1,176 @@
// Copyright 2018 CNI authors
//
// 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.
package testutils
import (
"crypto/rand"
"fmt"
"os"
"path"
"runtime"
"strings"
"sync"
"syscall"
"github.com/containernetworking/plugins/pkg/ns"
"golang.org/x/sys/unix"
)
func getNsRunDir() string {
xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR")
/// If XDG_RUNTIME_DIR is set, check if the current user owns /var/run. If
// the owner is different, we are most likely running in a user namespace.
// In that case use $XDG_RUNTIME_DIR/netns as runtime dir.
if xdgRuntimeDir != "" {
if s, err := os.Stat("/var/run"); err == nil {
st, ok := s.Sys().(*syscall.Stat_t)
if ok && int(st.Uid) != os.Geteuid() {
return path.Join(xdgRuntimeDir, "netns")
}
}
}
return "/var/run/netns"
}
// Creates a new persistent (bind-mounted) network namespace and returns an object
// representing that namespace, without switching to it.
func NewNS() (ns.NetNS, error) {
nsRunDir := getNsRunDir()
b := make([]byte, 16)
_, err := rand.Reader.Read(b)
if err != nil {
return nil, fmt.Errorf("failed to generate random netns name: %v", err)
}
// Create the directory for mounting network namespaces
// This needs to be a shared mountpoint in case it is mounted in to
// other namespaces (containers)
err = os.MkdirAll(nsRunDir, 0755)
if err != nil {
return nil, err
}
// Remount the namespace directory shared. This will fail if it is not
// already a mountpoint, so bind-mount it on to itself to "upgrade" it
// to a mountpoint.
err = unix.Mount("", nsRunDir, "none", unix.MS_SHARED|unix.MS_REC, "")
if err != nil {
if err != unix.EINVAL {
return nil, fmt.Errorf("mount --make-rshared %s failed: %q", nsRunDir, err)
}
// Recursively remount /var/run/netns on itself. The recursive flag is
// so that any existing netns bindmounts are carried over.
err = unix.Mount(nsRunDir, nsRunDir, "none", unix.MS_BIND|unix.MS_REC, "")
if err != nil {
return nil, fmt.Errorf("mount --rbind %s %s failed: %q", nsRunDir, nsRunDir, err)
}
// Now we can make it shared
err = unix.Mount("", nsRunDir, "none", unix.MS_SHARED|unix.MS_REC, "")
if err != nil {
return nil, fmt.Errorf("mount --make-rshared %s failed: %q", nsRunDir, err)
}
}
nsName := fmt.Sprintf("cnitest-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
// create an empty file at the mount point
nsPath := path.Join(nsRunDir, nsName)
mountPointFd, err := os.Create(nsPath)
if err != nil {
return nil, err
}
mountPointFd.Close()
// Ensure the mount point is cleaned up on errors; if the namespace
// was successfully mounted this will have no effect because the file
// is in-use
defer os.RemoveAll(nsPath)
var wg sync.WaitGroup
wg.Add(1)
// do namespace work in a dedicated goroutine, so that we can safely
// Lock/Unlock OSThread without upsetting the lock/unlock state of
// the caller of this function
go (func() {
defer wg.Done()
runtime.LockOSThread()
// Don't unlock. By not unlocking, golang will kill the OS thread when the
// goroutine is done (for go1.10+)
var origNS ns.NetNS
origNS, err = ns.GetNS(getCurrentThreadNetNSPath())
if err != nil {
return
}
defer origNS.Close()
// create a new netns on the current thread
err = unix.Unshare(unix.CLONE_NEWNET)
if err != nil {
return
}
// Put this thread back to the orig ns, since it might get reused (pre go1.10)
defer origNS.Set()
// bind mount the netns from the current thread (from /proc) onto the
// mount point. This causes the namespace to persist, even when there
// are no threads in the ns.
err = unix.Mount(getCurrentThreadNetNSPath(), nsPath, "none", unix.MS_BIND, "")
if err != nil {
err = fmt.Errorf("failed to bind mount ns at %s: %v", nsPath, err)
}
})()
wg.Wait()
if err != nil {
return nil, fmt.Errorf("failed to create namespace: %v", err)
}
return ns.GetNS(nsPath)
}
// UnmountNS unmounts the NS held by the netns object
func UnmountNS(ns ns.NetNS) error {
nsPath := ns.Path()
// Only unmount if it's been bind-mounted (don't touch namespaces in /proc...)
if strings.HasPrefix(nsPath, getNsRunDir()) {
if err := unix.Unmount(nsPath, 0); err != nil {
return fmt.Errorf("failed to unmount NS: at %s: %v", nsPath, err)
}
if err := os.Remove(nsPath); err != nil {
return fmt.Errorf("failed to remove ns path %s: %v", nsPath, err)
}
}
return nil
}
// getCurrentThreadNetNSPath copied from pkg/ns
func getCurrentThreadNetNSPath() string {
// /proc/self/ns/net returns the namespace of the main thread, not
// of whatever thread this goroutine is running on. Make sure we
// use the thread's net namespace since the thread is switching around
return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid())
}

55
pkg/testutils/ping.go Normal file
View File

@ -0,0 +1,55 @@
// Copyright 2017 CNI authors
//
// 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.
package testutils
import (
"bytes"
"fmt"
"os/exec"
"strconv"
"syscall"
)
// Ping shells out to the `ping` command. Returns nil if successful.
func Ping(saddr, daddr string, isV6 bool, timeoutSec int) error {
args := []string{
"-c", "1",
"-W", strconv.Itoa(timeoutSec),
"-I", saddr,
daddr,
}
bin := "ping"
if isV6 {
bin = "ping6"
}
cmd := exec.Command(bin, args...)
var stderr bytes.Buffer
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
switch e := err.(type) {
case *exec.ExitError:
return fmt.Errorf("%v exit status %d: %s",
args, e.Sys().(syscall.WaitStatus).ExitStatus(),
stderr.String())
default:
return err
}
}
return nil
}

View File

@ -1,106 +0,0 @@
// Copyright 2016 CNI authors
//
// 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.
package types_test
import (
"reflect"
. "github.com/containernetworking/cni/pkg/types"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/extensions/table"
. "github.com/onsi/gomega"
)
var _ = Describe("UnmarshallableBool UnmarshalText", func() {
DescribeTable("string to bool detection should succeed in all cases",
func(inputs []string, expected bool) {
for _, s := range inputs {
var ub UnmarshallableBool
err := ub.UnmarshalText([]byte(s))
Expect(err).ToNot(HaveOccurred())
Expect(ub).To(Equal(UnmarshallableBool(expected)))
}
},
Entry("parse to true", []string{"True", "true", "1"}, true),
Entry("parse to false", []string{"False", "false", "0"}, false),
)
Context("When passed an invalid value", func() {
It("should result in an error", func() {
var ub UnmarshallableBool
err := ub.UnmarshalText([]byte("invalid"))
Expect(err).To(HaveOccurred())
})
})
})
var _ = Describe("GetKeyField", func() {
type testcontainer struct {
Valid string `json:"valid,omitempty"`
}
var (
container = testcontainer{Valid: "valid"}
containerInterface = func(i interface{}) interface{} { return i }(&container)
containerValue = reflect.ValueOf(containerInterface)
)
Context("When a valid field is provided", func() {
It("should return the correct field", func() {
field := GetKeyField("Valid", containerValue)
Expect(field.String()).To(Equal("valid"))
})
})
})
var _ = Describe("LoadArgs", func() {
Context("When no arguments are passed", func() {
It("LoadArgs should succeed", func() {
err := LoadArgs("", struct{}{})
Expect(err).NotTo(HaveOccurred())
})
})
Context("When unknown arguments are passed and ignored", func() {
It("LoadArgs should succeed", func() {
ca := CommonArgs{}
err := LoadArgs("IgnoreUnknown=True;Unk=nown", &ca)
Expect(err).NotTo(HaveOccurred())
})
})
Context("When unknown arguments are passed and not ignored", func() {
It("LoadArgs should fail", func() {
ca := CommonArgs{}
err := LoadArgs("Unk=nown", &ca)
Expect(err).To(HaveOccurred())
})
})
Context("When unknown arguments are passed and explicitly not ignored", func() {
It("LoadArgs should fail", func() {
ca := CommonArgs{}
err := LoadArgs("IgnoreUnknown=0, Unk=nown", &ca)
Expect(err).To(HaveOccurred())
})
})
Context("When known arguments are passed", func() {
It("LoadArgs should succeed", func() {
ca := CommonArgs{}
err := LoadArgs("IgnoreUnknown=1", &ca)
Expect(err).NotTo(HaveOccurred())
})
})
})

View File

@ -0,0 +1,26 @@
// Copyright 2019 CNI authors
//
// 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.
// Buildversion is a destination for the linker trickery so we can auto
// set the build-version
package buildversion
import "fmt"
// This is overridden in the linker script
var BuildVersion = "version unknown"
func BuildString(pluginName string) string {
return fmt.Sprintf("CNI %s plugin %s", pluginName, BuildVersion)
}

View File

@ -0,0 +1,63 @@
// Copyright 2016 CNI authors
//
// 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.
package hwaddr
import (
"fmt"
"net"
)
const (
ipRelevantByteLen = 4
PrivateMACPrefixString = "0a:58"
)
var (
// private mac prefix safe to use
PrivateMACPrefix = []byte{0x0a, 0x58}
)
type SupportIp4OnlyErr struct{ msg string }
func (e SupportIp4OnlyErr) Error() string { return e.msg }
type MacParseErr struct{ msg string }
func (e MacParseErr) Error() string { return e.msg }
type InvalidPrefixLengthErr struct{ msg string }
func (e InvalidPrefixLengthErr) Error() string { return e.msg }
// GenerateHardwareAddr4 generates 48 bit virtual mac addresses based on the IP4 input.
func GenerateHardwareAddr4(ip net.IP, prefix []byte) (net.HardwareAddr, error) {
switch {
case ip.To4() == nil:
return nil, SupportIp4OnlyErr{msg: "GenerateHardwareAddr4 only supports valid IPv4 address as input"}
case len(prefix) != len(PrivateMACPrefix):
return nil, InvalidPrefixLengthErr{msg: fmt.Sprintf(
"Prefix has length %d instead of %d", len(prefix), len(PrivateMACPrefix)),
}
}
ipByteLen := len(ip)
return (net.HardwareAddr)(
append(
prefix,
ip[ipByteLen-ipRelevantByteLen:ipByteLen]...),
), nil
}

View File

@ -0,0 +1,27 @@
// Copyright 2016 CNI authors
//
// 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.
package hwaddr_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
)
func TestHwaddr(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "pkg/utils/hwaddr")
}

View File

@ -0,0 +1,74 @@
// Copyright 2016 CNI authors
//
// 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.
package hwaddr_test
import (
"net"
"github.com/containernetworking/plugins/pkg/utils/hwaddr"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Hwaddr", func() {
Context("Generate Hardware Address", func() {
It("generate hardware address based on ipv4 address", func() {
testCases := []struct {
ip net.IP
expectedMAC net.HardwareAddr
}{
{
ip: net.ParseIP("10.0.0.2"),
expectedMAC: (net.HardwareAddr)(append(hwaddr.PrivateMACPrefix, 0x0a, 0x00, 0x00, 0x02)),
},
{
ip: net.ParseIP("10.250.0.244"),
expectedMAC: (net.HardwareAddr)(append(hwaddr.PrivateMACPrefix, 0x0a, 0xfa, 0x00, 0xf4)),
},
{
ip: net.ParseIP("172.17.0.2"),
expectedMAC: (net.HardwareAddr)(append(hwaddr.PrivateMACPrefix, 0xac, 0x11, 0x00, 0x02)),
},
{
ip: net.IPv4(byte(172), byte(17), byte(0), byte(2)),
expectedMAC: (net.HardwareAddr)(append(hwaddr.PrivateMACPrefix, 0xac, 0x11, 0x00, 0x02)),
},
}
for _, tc := range testCases {
mac, err := hwaddr.GenerateHardwareAddr4(tc.ip, hwaddr.PrivateMACPrefix)
Expect(err).NotTo(HaveOccurred())
Expect(mac).To(Equal(tc.expectedMAC))
}
})
It("return error if input is not ipv4 address", func() {
testCases := []net.IP{
net.ParseIP(""),
net.ParseIP("2001:db8:0:1:1:1:1:1"),
}
for _, tc := range testCases {
_, err := hwaddr.GenerateHardwareAddr4(tc, hwaddr.PrivateMACPrefix)
Expect(err).To(BeAssignableToTypeOf(hwaddr.SupportIp4OnlyErr{}))
}
})
It("return error if prefix is invalid", func() {
_, err := hwaddr.GenerateHardwareAddr4(net.ParseIP("10.0.0.2"), []byte{0x58})
Expect(err).To(BeAssignableToTypeOf(hwaddr.InvalidPrefixLengthErr{}))
})
})
})

121
pkg/utils/iptables.go Normal file
View File

@ -0,0 +1,121 @@
// Copyright 2017 CNI authors
//
// 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.
package utils
import (
"errors"
"fmt"
"github.com/coreos/go-iptables/iptables"
)
const statusChainExists = 1
// EnsureChain idempotently creates the iptables chain. It does not
// return an error if the chain already exists.
func EnsureChain(ipt *iptables.IPTables, table, chain string) error {
if ipt == nil {
return errors.New("failed to ensure iptable chain: IPTables was nil")
}
exists, err := ChainExists(ipt, table, chain)
if err != nil {
return fmt.Errorf("failed to list iptables chains: %v", err)
}
if !exists {
err = ipt.NewChain(table, chain)
if err != nil {
eerr, eok := err.(*iptables.Error)
if eok && eerr.ExitStatus() != statusChainExists {
return err
}
}
}
return nil
}
// ChainExists checks whether an iptables chain exists.
func ChainExists(ipt *iptables.IPTables, table, chain string) (bool, error) {
if ipt == nil {
return false, errors.New("failed to check iptable chain: IPTables was nil")
}
chains, err := ipt.ListChains(table)
if err != nil {
return false, err
}
for _, ch := range chains {
if ch == chain {
return true, nil
}
}
return false, nil
}
// DeleteRule idempotently delete the iptables rule in the specified table/chain.
// It does not return an error if the referring chain doesn't exist
func DeleteRule(ipt *iptables.IPTables, table, chain string, rulespec ...string) error {
if ipt == nil {
return errors.New("failed to ensure iptable chain: IPTables was nil")
}
if err := ipt.Delete(table, chain, rulespec...); err != nil {
eerr, eok := err.(*iptables.Error)
switch {
case eok && eerr.IsNotExist():
// swallow here, the chain was already deleted
return nil
case eok && eerr.ExitStatus() == 2:
// swallow here, invalid command line parameter because the referring rule is missing
return nil
default:
return fmt.Errorf("Failed to delete referring rule %s %s: %v", table, chain, err)
}
}
return nil
}
// DeleteChain idempotently deletes the specified table/chain.
// It does not return an errors if the chain does not exist
func DeleteChain(ipt *iptables.IPTables, table, chain string) error {
if ipt == nil {
return errors.New("failed to ensure iptable chain: IPTables was nil")
}
err := ipt.DeleteChain(table, chain)
eerr, eok := err.(*iptables.Error)
switch {
case eok && eerr.IsNotExist():
// swallow here, the chain was already deleted
return nil
default:
return err
}
}
// ClearChain idempotently clear the iptables rules in the specified table/chain.
// If the chain does not exist, a new one will be created
func ClearChain(ipt *iptables.IPTables, table, chain string) error {
if ipt == nil {
return errors.New("failed to ensure iptable chain: IPTables was nil")
}
err := ipt.ClearChain(table, chain)
eerr, eok := err.(*iptables.Error)
switch {
case eok && eerr.IsNotExist():
// swallow here, the chain was already deleted
return EnsureChain(ipt, table, chain)
default:
return err
}
}

View File

@ -0,0 +1,97 @@
// Copyright 2017-2018 CNI authors
//
// 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.
package utils
import (
"fmt"
"math/rand"
"runtime"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
"github.com/coreos/go-iptables/iptables"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
const TABLE = "filter" // We'll monkey around here
var _ = Describe("chain tests", func() {
var testChain string
var ipt *iptables.IPTables
var cleanup func()
BeforeEach(func() {
// Save a reference to the original namespace,
// Add a new NS
currNs, err := ns.GetCurrentNS()
Expect(err).NotTo(HaveOccurred())
testNs, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
testChain = fmt.Sprintf("cni-test-%d", rand.Intn(10000000))
ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv4)
Expect(err).NotTo(HaveOccurred())
runtime.LockOSThread()
err = testNs.Set()
Expect(err).NotTo(HaveOccurred())
cleanup = func() {
if ipt == nil {
return
}
ipt.ClearChain(TABLE, testChain)
ipt.DeleteChain(TABLE, testChain)
currNs.Set()
}
})
AfterEach(func() {
cleanup()
})
Describe("EnsureChain", func() {
It("creates chains idempotently", func() {
err := EnsureChain(ipt, TABLE, testChain)
Expect(err).NotTo(HaveOccurred())
// Create it again!
err = EnsureChain(ipt, TABLE, testChain)
Expect(err).NotTo(HaveOccurred())
})
})
Describe("DeleteChain", func() {
It("delete chains idempotently", func() {
// Create chain
err := EnsureChain(ipt, TABLE, testChain)
Expect(err).NotTo(HaveOccurred())
// Delete chain
err = DeleteChain(ipt, TABLE, testChain)
Expect(err).NotTo(HaveOccurred())
// Delete it again!
err = DeleteChain(ipt, TABLE, testChain)
Expect(err).NotTo(HaveOccurred())
})
})
})

View File

@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// build +linux
package sysctl
import (
@ -26,7 +24,7 @@ import (
// Sysctl provides a method to set/get values from /proc/sys - in linux systems
// new interface to set/get values of variables formerly handled by sysctl syscall
// If optional `params` have only one string value - this function will
// set this value into coresponding sysctl variable
// set this value into corresponding sysctl variable
func Sysctl(name string, params ...string) (string, error) {
if len(params) > 1 {
return "", fmt.Errorf("unexcepted additional parameters")
@ -37,7 +35,7 @@ func Sysctl(name string, params ...string) (string, error) {
}
func getSysctl(name string) (string, error) {
fullName := filepath.Join("/proc/sys", strings.Replace(name, ".", "/", -1))
fullName := filepath.Join("/proc/sys", toNormalName(name))
fullName = filepath.Clean(fullName)
data, err := ioutil.ReadFile(fullName)
if err != nil {
@ -48,7 +46,7 @@ func getSysctl(name string) (string, error) {
}
func setSysctl(name, value string) (string, error) {
fullName := filepath.Join("/proc/sys", strings.Replace(name, ".", "/", -1))
fullName := filepath.Join("/proc/sys", toNormalName(name))
fullName = filepath.Clean(fullName)
if err := ioutil.WriteFile(fullName, []byte(value), 0644); err != nil {
return "", err
@ -56,3 +54,27 @@ func setSysctl(name, value string) (string, error) {
return getSysctl(name)
}
// Normalize names by using slash as separator
// Sysctl names can use dots or slashes as separator:
// - if dots are used, dots and slashes are interchanged.
// - if slashes are used, slashes and dots are left intact.
// Separator in use is determined by first occurrence.
func toNormalName(name string) string {
interchange := false
for _, c := range name {
if c == '.' {
interchange = true
break
}
if c == '/' {
break
}
}
if interchange {
r := strings.NewReplacer(".", "/", "/", ".")
return r.Replace(name)
}
return name
}

View File

@ -0,0 +1,114 @@
// Copyright 2017-2020 CNI authors
//
// 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.
package sysctl_test
import (
"fmt"
"math/rand"
"runtime"
"strings"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
"github.com/containernetworking/plugins/pkg/utils/sysctl"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/vishvananda/netlink"
)
const (
sysctlDotKeyTemplate = "net.ipv4.conf.%s.proxy_arp"
sysctlSlashKeyTemplate = "net/ipv4/conf/%s/proxy_arp"
)
var _ = Describe("Sysctl tests", func() {
var testIfaceName string
var cleanup func()
BeforeEach(func() {
// Save a reference to the original namespace,
// Add a new NS
currNs, err := ns.GetCurrentNS()
Expect(err).NotTo(HaveOccurred())
testNs, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
testIfaceName = fmt.Sprintf("cnitest.%d", rand.Intn(100000))
testIface := &netlink.Dummy{
LinkAttrs: netlink.LinkAttrs{
Name: testIfaceName,
Namespace: netlink.NsFd(int(testNs.Fd())),
},
}
err = netlink.LinkAdd(testIface)
Expect(err).NotTo(HaveOccurred())
runtime.LockOSThread()
err = testNs.Set()
Expect(err).NotTo(HaveOccurred())
cleanup = func() {
netlink.LinkDel(testIface)
currNs.Set()
}
})
AfterEach(func() {
cleanup()
})
Describe("Sysctl", func() {
It("reads keys with dot separators", func() {
sysctlIfaceName := strings.Replace(testIfaceName, ".", "/", -1)
sysctlKey := fmt.Sprintf(sysctlDotKeyTemplate, sysctlIfaceName)
_, err := sysctl.Sysctl(sysctlKey)
Expect(err).NotTo(HaveOccurred())
})
})
Describe("Sysctl", func() {
It("reads keys with slash separators", func() {
sysctlKey := fmt.Sprintf(sysctlSlashKeyTemplate, testIfaceName)
_, err := sysctl.Sysctl(sysctlKey)
Expect(err).NotTo(HaveOccurred())
})
})
Describe("Sysctl", func() {
It("writes keys with dot separators", func() {
sysctlIfaceName := strings.Replace(testIfaceName, ".", "/", -1)
sysctlKey := fmt.Sprintf(sysctlDotKeyTemplate, sysctlIfaceName)
_, err := sysctl.Sysctl(sysctlKey, "1")
Expect(err).NotTo(HaveOccurred())
})
})
Describe("Sysctl", func() {
It("writes keys with slash separators", func() {
sysctlKey := fmt.Sprintf(sysctlSlashKeyTemplate, testIfaceName)
_, err := sysctl.Sysctl(sysctlKey, "1")
Expect(err).NotTo(HaveOccurred())
})
})
})

View File

@ -0,0 +1,27 @@
// Copyright 2017-2020 CNI authors
//
// 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.
package sysctl_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestSysctl(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Sysctl Suite")
}

View File

@ -22,16 +22,22 @@ import (
const (
maxChainLength = 28
chainPrefix = "CNI-"
prefixLength = len(chainPrefix)
)
// Generates a chain name to be used with iptables.
// Ensures that the generated chain name is exactly
// maxChainLength chars in length
// FormatChainName generates a chain name to be used
// with iptables. Ensures that the generated chain
// name is exactly maxChainLength chars in length.
func FormatChainName(name string, id string) string {
chainBytes := sha512.Sum512([]byte(name + id))
chain := fmt.Sprintf("%s%x", chainPrefix, chainBytes)
return chain[:maxChainLength]
return MustFormatChainNameWithPrefix(name, id, "")
}
// MustFormatChainNameWithPrefix generates a chain name similar
// to FormatChainName, but adds a custom prefix between
// chainPrefix and unique identifier. Ensures that the
// generated chain name is exactly maxChainLength chars in length.
// Panics if the given prefix is too long.
func MustFormatChainNameWithPrefix(name string, id string, prefix string) string {
return MustFormatHashWithPrefix(maxChainLength, chainPrefix+prefix, name+id)
}
// FormatComment returns a comment used for easier
@ -39,3 +45,16 @@ func FormatChainName(name string, id string) string {
func FormatComment(name string, id string) string {
return fmt.Sprintf("name: %q id: %q", name, id)
}
const MaxHashLen = sha512.Size * 2
// MustFormatHashWithPrefix returns a string of given length that begins with the
// given prefix. It is filled with entropy based on the given string toHash.
func MustFormatHashWithPrefix(length int, prefix string, toHash string) string {
if len(prefix) >= length || length > MaxHashLen {
panic("invalid length")
}
output := sha512.Sum512([]byte(toHash))
return fmt.Sprintf("%s%x", prefix, output)[:length]
}

View File

@ -23,5 +23,5 @@ import (
func TestUtils(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Utils Suite")
RunSpecs(t, "pkg/utils")
}

View File

@ -15,37 +15,151 @@
package utils
import (
"fmt"
"strings"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Utils", func() {
It("must format a short name", func() {
chain := FormatChainName("test", "1234")
Expect(len(chain)).To(Equal(maxChainLength))
Expect(chain).To(Equal("CNI-2bbe0c48b91a7d1b8a6753a8"))
Describe("FormatChainName", func() {
It("must format a short name", func() {
chain := FormatChainName("test", "1234")
Expect(len(chain)).To(Equal(maxChainLength))
Expect(chain).To(Equal("CNI-2bbe0c48b91a7d1b8a6753a8"))
})
It("must truncate a long name", func() {
chain := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
Expect(len(chain)).To(Equal(maxChainLength))
Expect(chain).To(Equal("CNI-374f33fe84ab0ed84dcdebe3"))
})
It("must be predictable", func() {
chain1 := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
chain2 := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
Expect(len(chain1)).To(Equal(maxChainLength))
Expect(len(chain2)).To(Equal(maxChainLength))
Expect(chain1).To(Equal(chain2))
})
It("must change when a character changes", func() {
chain1 := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
chain2 := FormatChainName("testalongnamethatdoesnotmakesense", "1235")
Expect(len(chain1)).To(Equal(maxChainLength))
Expect(len(chain2)).To(Equal(maxChainLength))
Expect(chain1).To(Equal("CNI-374f33fe84ab0ed84dcdebe3"))
Expect(chain1).NotTo(Equal(chain2))
})
})
It("must truncate a long name", func() {
chain := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
Expect(len(chain)).To(Equal(maxChainLength))
Expect(chain).To(Equal("CNI-374f33fe84ab0ed84dcdebe3"))
Describe("MustFormatChainNameWithPrefix", func() {
It("generates a chain name with a prefix", func() {
chain := MustFormatChainNameWithPrefix("test", "1234", "PREFIX-")
Expect(len(chain)).To(Equal(maxChainLength))
Expect(chain).To(Equal("CNI-PREFIX-2bbe0c48b91a7d1b8"))
})
It("must format a short name", func() {
chain := MustFormatChainNameWithPrefix("test", "1234", "PREFIX-")
Expect(len(chain)).To(Equal(maxChainLength))
Expect(chain).To(Equal("CNI-PREFIX-2bbe0c48b91a7d1b8"))
})
It("must truncate a long name", func() {
chain := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-")
Expect(len(chain)).To(Equal(maxChainLength))
Expect(chain).To(Equal("CNI-PREFIX-374f33fe84ab0ed84"))
})
It("must be predictable", func() {
chain1 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-")
chain2 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-")
Expect(len(chain1)).To(Equal(maxChainLength))
Expect(len(chain2)).To(Equal(maxChainLength))
Expect(chain1).To(Equal(chain2))
})
It("must change when a character changes", func() {
chain1 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1234", "PREFIX-")
chain2 := MustFormatChainNameWithPrefix("testalongnamethatdoesnotmakesense", "1235", "PREFIX-")
Expect(len(chain1)).To(Equal(maxChainLength))
Expect(len(chain2)).To(Equal(maxChainLength))
Expect(chain1).To(Equal("CNI-PREFIX-374f33fe84ab0ed84"))
Expect(chain1).NotTo(Equal(chain2))
})
It("panics when prefix is too large", func() {
longPrefix := strings.Repeat("PREFIX-", 4)
Expect(func() {
MustFormatChainNameWithPrefix("test", "1234", longPrefix)
}).To(Panic())
})
})
It("must be predictable", func() {
chain1 := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
chain2 := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
Expect(len(chain1)).To(Equal(maxChainLength))
Expect(len(chain2)).To(Equal(maxChainLength))
Expect(chain1).To(Equal(chain2))
Describe("MustFormatHashWithPrefix", func() {
It("always returns a string with the given prefix", func() {
Expect(MustFormatHashWithPrefix(10, "AAA", "some string")).To(HavePrefix("AAA"))
Expect(MustFormatHashWithPrefix(10, "foo", "some string")).To(HavePrefix("foo"))
Expect(MustFormatHashWithPrefix(10, "bar", "some string")).To(HavePrefix("bar"))
})
It("always returns a string of the given length", func() {
Expect(MustFormatHashWithPrefix(10, "AAA", "some string")).To(HaveLen(10))
Expect(MustFormatHashWithPrefix(15, "AAA", "some string")).To(HaveLen(15))
Expect(MustFormatHashWithPrefix(5, "AAA", "some string")).To(HaveLen(5))
})
It("is deterministic", func() {
val1 := MustFormatHashWithPrefix(10, "AAA", "some string")
val2 := MustFormatHashWithPrefix(10, "AAA", "some string")
val3 := MustFormatHashWithPrefix(10, "AAA", "some string")
Expect(val1).To(Equal(val2))
Expect(val1).To(Equal(val3))
})
It("is (nearly) perfect (injective function)", func() {
hashes := map[string]int{}
for i := 0; i < 1000; i++ {
name := fmt.Sprintf("string %d", i)
hashes[MustFormatHashWithPrefix(8, "", name)]++
}
for key, count := range hashes {
Expect(count).To(Equal(1), "for key "+key+" got non-unique correspondence")
}
})
assertPanicWith := func(f func(), expectedErrorMessage string) {
defer func() {
Expect(recover()).To(Equal(expectedErrorMessage))
}()
f()
Fail("function should have panicked but did not")
}
It("panics when prefix is longer than the length", func() {
assertPanicWith(
func() { MustFormatHashWithPrefix(3, "AAA", "some string") },
"invalid length",
)
})
It("panics when length is not positive", func() {
assertPanicWith(
func() { MustFormatHashWithPrefix(0, "", "some string") },
"invalid length",
)
})
It("panics when length is larger than MaxLen", func() {
assertPanicWith(
func() { MustFormatHashWithPrefix(MaxHashLen+1, "", "some string") },
"invalid length",
)
})
})
It("must change when a character changes", func() {
chain1 := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
chain2 := FormatChainName("testalongnamethatdoesnotmakesense", "1235")
Expect(len(chain1)).To(Equal(maxChainLength))
Expect(len(chain2)).To(Equal(maxChainLength))
Expect(chain1).To(Equal("CNI-374f33fe84ab0ed84dcdebe3"))
Expect(chain1).NotTo(Equal(chain2))
})
})

View File

@ -3,7 +3,7 @@
## Overview
With dhcp plugin the containers can get an IP allocated by a DHCP server already running on your network.
This can be especially useful with plugin types such as [macvlan](https://github.com/containernetworking/cni/blob/master/Documentation/macvlan.md).
This can be especially useful with plugin types such as [macvlan](../../main/macvlan/README.md).
Because a DHCP lease must be periodically renewed for the duration of container lifetime, a separate daemon is required to be running.
The same plugin binary can also be run in the daemon mode.
@ -16,6 +16,10 @@ $ rm -f /run/cni/dhcp.sock
$ ./dhcp daemon
```
If given `-pidfile <path>` arguments after 'daemon', the dhcp plugin will write
its PID to the given file.
If given `-hostprefix <prefix>` arguments after 'daemon', the dhcp plugin will use this prefix for DHCP socket as `<prefix>/run/cni/dhcp.sock`. You can use this prefix for references to the host filesystem, e.g. to access netns and the unix socket.
Alternatively, you can use systemd socket activation protocol.
Be sure that the .socket file uses /run/cni/dhcp.sock as the socket path.

121
plugins/ipam/dhcp/client.go Normal file
View File

@ -0,0 +1,121 @@
package main
import (
"github.com/d2g/dhcp4"
"github.com/d2g/dhcp4client"
)
const (
MaxDHCPLen = 576
)
//Send the Discovery Packet to the Broadcast Channel
func DhcpSendDiscoverPacket(c *dhcp4client.Client, options dhcp4.Options) (dhcp4.Packet, error) {
discoveryPacket := c.DiscoverPacket()
for opt, data := range options {
discoveryPacket.AddOption(opt, data)
}
discoveryPacket.PadToMinSize()
return discoveryPacket, c.SendPacket(discoveryPacket)
}
//Send Request Based On the offer Received.
func DhcpSendRequest(c *dhcp4client.Client, options dhcp4.Options, offerPacket *dhcp4.Packet) (dhcp4.Packet, error) {
requestPacket := c.RequestPacket(offerPacket)
for opt, data := range options {
requestPacket.AddOption(opt, data)
}
requestPacket.PadToMinSize()
return requestPacket, c.SendPacket(requestPacket)
}
//Send Decline to the received acknowledgement.
func DhcpSendDecline(c *dhcp4client.Client, acknowledgementPacket *dhcp4.Packet, options dhcp4.Options) (dhcp4.Packet, error) {
declinePacket := c.DeclinePacket(acknowledgementPacket)
for opt, data := range options {
declinePacket.AddOption(opt, data)
}
declinePacket.PadToMinSize()
return declinePacket, c.SendPacket(declinePacket)
}
//Lets do a Full DHCP Request.
func DhcpRequest(c *dhcp4client.Client, options dhcp4.Options) (bool, dhcp4.Packet, error) {
discoveryPacket, err := DhcpSendDiscoverPacket(c, options)
if err != nil {
return false, discoveryPacket, err
}
offerPacket, err := c.GetOffer(&discoveryPacket)
if err != nil {
return false, offerPacket, err
}
requestPacket, err := DhcpSendRequest(c, options, &offerPacket)
if err != nil {
return false, requestPacket, err
}
acknowledgement, err := c.GetAcknowledgement(&requestPacket)
if err != nil {
return false, acknowledgement, err
}
acknowledgementOptions := acknowledgement.ParseOptions()
if dhcp4.MessageType(acknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK {
return false, acknowledgement, nil
}
return true, acknowledgement, nil
}
//Renew a lease backed on the Acknowledgement Packet.
//Returns Successful, The AcknoledgementPacket, Any Errors
func DhcpRenew(c *dhcp4client.Client, acknowledgement dhcp4.Packet, options dhcp4.Options) (bool, dhcp4.Packet, error) {
renewRequest := c.RenewalRequestPacket(&acknowledgement)
for opt, data := range options {
renewRequest.AddOption(opt, data)
}
renewRequest.PadToMinSize()
err := c.SendPacket(renewRequest)
if err != nil {
return false, renewRequest, err
}
newAcknowledgement, err := c.GetAcknowledgement(&renewRequest)
if err != nil {
return false, newAcknowledgement, err
}
newAcknowledgementOptions := newAcknowledgement.ParseOptions()
if dhcp4.MessageType(newAcknowledgementOptions[dhcp4.OptionDHCPMessageType][0]) != dhcp4.ACK {
return false, newAcknowledgement, nil
}
return true, newAcknowledgement, nil
}
//Release a lease backed on the Acknowledgement Packet.
//Returns Any Errors
func DhcpRelease(c *dhcp4client.Client, acknowledgement dhcp4.Packet, options dhcp4.Options) error {
release := c.ReleasePacket(&acknowledgement)
for opt, data := range options {
release.AddOption(opt, data)
}
release.PadToMinSize()
return c.SendPacket(release)
}

View File

@ -18,7 +18,7 @@ import (
"encoding/json"
"errors"
"fmt"
"log"
"io/ioutil"
"net"
"net/http"
"net/rpc"
@ -29,6 +29,7 @@ import (
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/coreos/go-systemd/activation"
)
@ -38,8 +39,9 @@ const resendCount = 3
var errNoMoreTries = errors.New("no more tries")
type DHCP struct {
mux sync.Mutex
leases map[string]*DHCPLease
mux sync.Mutex
leases map[string]*DHCPLease
hostNetnsPrefix string
}
func newDHCP() *DHCP {
@ -48,16 +50,21 @@ func newDHCP() *DHCP {
}
}
func generateClientID(containerID string, netName string, ifName string) string {
return containerID + "/" + netName + "/" + ifName
}
// Allocate acquires an IP from a DHCP server for a specified container.
// The acquired lease will be maintained until Release() is called.
func (d *DHCP) Allocate(args *skel.CmdArgs, result *types.Result) error {
func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error {
conf := types.NetConf{}
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
return fmt.Errorf("error parsing netconf: %v", err)
}
clientID := args.ContainerID + "/" + conf.Name
l, err := AcquireLease(clientID, args.Netns, args.IfName)
clientID := generateClientID(args.ContainerID, conf.Name, args.IfName)
hostNetns := d.hostNetnsPrefix + args.Netns
l, err := AcquireLease(clientID, hostNetns, args.IfName)
if err != nil {
return err
}
@ -68,13 +75,14 @@ func (d *DHCP) Allocate(args *skel.CmdArgs, result *types.Result) error {
return err
}
d.setLease(args.ContainerID, conf.Name, l)
d.setLease(clientID, l)
result.IP4 = &types.IPConfig{
IP: *ipn,
result.IPs = []*current.IPConfig{{
Version: "4",
Address: *ipn,
Gateway: l.Gateway(),
Routes: l.Routes(),
}
}}
result.Routes = l.Routes()
return nil
}
@ -87,36 +95,46 @@ func (d *DHCP) Release(args *skel.CmdArgs, reply *struct{}) error {
return fmt.Errorf("error parsing netconf: %v", err)
}
if l := d.getLease(args.ContainerID, conf.Name); l != nil {
clientID := generateClientID(args.ContainerID, conf.Name, args.IfName)
if l := d.getLease(clientID); l != nil {
l.Stop()
return nil
d.clearLease(clientID)
}
return fmt.Errorf("lease not found: %v/%v", args.ContainerID, conf.Name)
return nil
}
func (d *DHCP) getLease(contID, netName string) *DHCPLease {
func (d *DHCP) getLease(clientID string) *DHCPLease {
d.mux.Lock()
defer d.mux.Unlock()
// TODO(eyakubovich): hash it to avoid collisions
l, ok := d.leases[contID+netName]
l, ok := d.leases[clientID]
if !ok {
return nil
}
return l
}
func (d *DHCP) setLease(contID, netName string, l *DHCPLease) {
func (d *DHCP) setLease(clientID string, l *DHCPLease) {
d.mux.Lock()
defer d.mux.Unlock()
// TODO(eyakubovich): hash it to avoid collisions
d.leases[contID+netName] = l
d.leases[clientID] = l
}
func getListener() (net.Listener, error) {
l, err := activation.Listeners(true)
//func (d *DHCP) clearLease(contID, netName, ifName string) {
func (d *DHCP) clearLease(clientID string) {
d.mux.Lock()
defer d.mux.Unlock()
// TODO(eyakubovich): hash it to avoid collisions
delete(d.leases, clientID)
}
func getListener(socketPath string) (net.Listener, error) {
l, err := activation.Listeners()
if err != nil {
return nil, err
}
@ -139,19 +157,30 @@ func getListener() (net.Listener, error) {
}
}
func runDaemon() {
func runDaemon(pidfilePath string, hostPrefix string, socketPath string) error {
// since other goroutines (on separate threads) will change namespaces,
// ensure the RPC server does not get scheduled onto those
runtime.LockOSThread()
l, err := getListener()
// Write the pidfile
if pidfilePath != "" {
if !filepath.IsAbs(pidfilePath) {
return fmt.Errorf("Error writing pidfile %q: path not absolute", pidfilePath)
}
if err := ioutil.WriteFile(pidfilePath, []byte(fmt.Sprintf("%d", os.Getpid())), 0644); err != nil {
return fmt.Errorf("Error writing pidfile %q: %v", pidfilePath, err)
}
}
l, err := getListener(hostPrefix + socketPath)
if err != nil {
log.Printf("Error getting listener: %v", err)
return
return fmt.Errorf("Error getting listener: %v", err)
}
dhcp := newDHCP()
dhcp.hostNetnsPrefix = hostPrefix
rpc.Register(dhcp)
rpc.HandleHTTP()
http.Serve(l, nil)
return nil
}

View File

@ -0,0 +1,183 @@
// Copyright 2015-2018 CNI authors
//
// 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.
package main
import (
"fmt"
"net"
"os"
"os/exec"
"sync"
"time"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
"github.com/vishvananda/netlink"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("DHCP Multiple Lease Operations", func() {
var originalNS, targetNS ns.NetNS
var dhcpServerStopCh chan bool
var dhcpServerDone *sync.WaitGroup
var clientCmd *exec.Cmd
var socketPath string
var tmpDir string
var serverIP net.IPNet
var err error
BeforeEach(func() {
dhcpServerStopCh, serverIP, socketPath, originalNS, targetNS, err = dhcpSetupOriginalNS()
Expect(err).NotTo(HaveOccurred())
// Move the container side to the container's NS
err = targetNS.Do(func(_ ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName(contVethName0)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetUp(link)
Expect(err).NotTo(HaveOccurred())
link1, err := netlink.LinkByName(contVethName1)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetUp(link1)
Expect(err).NotTo(HaveOccurred())
return nil
})
// Start the DHCP server
dhcpServerDone, err = dhcpServerStart(originalNS, net.IPv4(192, 168, 1, 5), serverIP.IP, 2, dhcpServerStopCh)
Expect(err).NotTo(HaveOccurred())
// Start the DHCP client daemon
dhcpPluginPath, err := exec.LookPath("dhcp")
Expect(err).NotTo(HaveOccurred())
clientCmd = exec.Command(dhcpPluginPath, "daemon", "-socketpath", socketPath)
err = clientCmd.Start()
Expect(err).NotTo(HaveOccurred())
Expect(clientCmd.Process).NotTo(BeNil())
// Wait up to 15 seconds for the client socket
Eventually(func() bool {
_, err := os.Stat(socketPath)
return err == nil
}, time.Second*15, time.Second/4).Should(BeTrue())
})
AfterEach(func() {
dhcpServerStopCh <- true
dhcpServerDone.Wait()
clientCmd.Process.Kill()
clientCmd.Wait()
Expect(originalNS.Close()).To(Succeed())
Expect(targetNS.Close()).To(Succeed())
defer os.RemoveAll(tmpDir)
})
It("configures multiple links with multiple ADD/DEL", func() {
conf := fmt.Sprintf(`{
"cniVersion": "0.3.1",
"name": "mynet",
"type": "bridge",
"bridge": "%s",
"ipam": {
"type": "dhcp",
"daemonSocketPath": "%s"
}
}`, hostBridgeName, socketPath)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: contVethName0,
StdinData: []byte(conf),
}
var addResult *current.Result
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
r, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
addResult, err = current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(len(addResult.IPs)).To(Equal(1))
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24"))
return nil
})
Expect(err).NotTo(HaveOccurred())
args = &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: contVethName1,
StdinData: []byte(conf),
}
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
r, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
addResult, err = current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(len(addResult.IPs)).To(Equal(1))
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.6/24"))
return nil
})
Expect(err).NotTo(HaveOccurred())
args = &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: contVethName1,
StdinData: []byte(conf),
}
err = originalNS.Do(func(ns.NetNS) error {
return testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
})
Expect(err).NotTo(HaveOccurred())
args = &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: contVethName0,
StdinData: []byte(conf),
}
err = originalNS.Do(func(ns.NetNS) error {
return testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
})
Expect(err).NotTo(HaveOccurred())
})
})

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package invoke_test
package main
import (
. "github.com/onsi/ginkgo"
@ -21,7 +21,7 @@ import (
"testing"
)
func TestInvoke(t *testing.T) {
func TestDHCP(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Invoke Suite")
RunSpecs(t, "plugins/ipam/dhcp")
}

View File

@ -0,0 +1,618 @@
// Copyright 2015-2018 CNI authors
//
// 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.
package main
import (
"fmt"
"io/ioutil"
"net"
"os"
"os/exec"
"path/filepath"
"sync"
"time"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils"
"github.com/vishvananda/netlink"
"github.com/d2g/dhcp4"
"github.com/d2g/dhcp4server"
"github.com/d2g/dhcp4server/leasepool"
"github.com/d2g/dhcp4server/leasepool/memorypool"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func getTmpDir() (string, error) {
tmpDir, err := ioutil.TempDir(cniDirPrefix, "dhcp")
if err == nil {
tmpDir = filepath.ToSlash(tmpDir)
}
return tmpDir, err
}
func dhcpServerStart(netns ns.NetNS, leaseIP, serverIP net.IP, numLeases int, stopCh <-chan bool) (*sync.WaitGroup, error) {
// Add the expected IP to the pool
lp := memorypool.MemoryPool{}
Expect(numLeases).To(BeNumerically(">", 0))
// Currently tests only need at most 2
Expect(numLeases).To(BeNumerically("<=", 2))
// tests expect first lease to be at address 192.168.1.5
for i := 5; i < numLeases+5; i++ {
err := lp.AddLease(leasepool.Lease{IP: dhcp4.IPAdd(net.IPv4(192, 168, 1, byte(i)), 0)})
if err != nil {
return nil, fmt.Errorf("error adding IP to DHCP pool: %v", err)
}
}
dhcpServer, err := dhcp4server.New(
net.IPv4(192, 168, 1, 1),
&lp,
dhcp4server.SetLocalAddr(net.UDPAddr{IP: net.IPv4(0, 0, 0, 0), Port: 67}),
dhcp4server.SetRemoteAddr(net.UDPAddr{IP: net.IPv4bcast, Port: 68}),
dhcp4server.LeaseDuration(time.Minute*15),
)
if err != nil {
return nil, fmt.Errorf("failed to create DHCP server: %v", err)
}
stopWg := sync.WaitGroup{}
stopWg.Add(2)
startWg := sync.WaitGroup{}
startWg.Add(2)
// Run DHCP server in a goroutine so it doesn't block the main thread
go func() {
defer GinkgoRecover()
err = netns.Do(func(ns.NetNS) error {
startWg.Done()
if err := dhcpServer.ListenAndServe(); err != nil {
// Log, but don't trap errors; the server will
// always report an error when stopped
GinkgoT().Logf("DHCP server finished with error: %v", err)
}
return nil
})
stopWg.Done()
// Trap any errors after the Done, to allow the main test thread
// to continue and clean up. Otherwise the test hangs.
Expect(err).NotTo(HaveOccurred())
}()
// Stop DHCP server in another goroutine for the same reason
go func() {
startWg.Done()
<-stopCh
dhcpServer.Shutdown()
stopWg.Done()
}()
startWg.Wait()
return &stopWg, nil
}
const (
hostVethName string = "dhcp0"
contVethName string = "eth0"
cniDirPrefix string = "/var/run/cni"
)
var _ = BeforeSuite(func() {
err := os.MkdirAll(cniDirPrefix, 0700)
Expect(err).NotTo(HaveOccurred())
})
var _ = Describe("DHCP Operations", func() {
var originalNS, targetNS ns.NetNS
var dhcpServerStopCh chan bool
var dhcpServerDone *sync.WaitGroup
var clientCmd *exec.Cmd
var socketPath string
var tmpDir string
var err error
BeforeEach(func() {
dhcpServerStopCh = make(chan bool)
tmpDir, err = getTmpDir()
Expect(err).NotTo(HaveOccurred())
socketPath = filepath.Join(tmpDir, "dhcp.sock")
// Create a new NetNS so we don't modify the host
var err error
originalNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
targetNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
serverIP := net.IPNet{
IP: net.IPv4(192, 168, 1, 1),
Mask: net.IPv4Mask(255, 255, 255, 0),
}
// Create a veth pair in the "host" (original) NS
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
err = netlink.LinkAdd(&netlink.Veth{
LinkAttrs: netlink.LinkAttrs{
Name: hostVethName,
},
PeerName: contVethName,
})
Expect(err).NotTo(HaveOccurred())
host, err := netlink.LinkByName(hostVethName)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetUp(host)
Expect(err).NotTo(HaveOccurred())
err = netlink.AddrAdd(host, &netlink.Addr{IPNet: &serverIP})
Expect(err).NotTo(HaveOccurred())
err = netlink.RouteAdd(&netlink.Route{
LinkIndex: host.Attrs().Index,
Scope: netlink.SCOPE_UNIVERSE,
Dst: &net.IPNet{
IP: net.IPv4(0, 0, 0, 0),
Mask: net.IPv4Mask(0, 0, 0, 0),
},
})
Expect(err).NotTo(HaveOccurred())
cont, err := netlink.LinkByName(contVethName)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetNsFd(cont, int(targetNS.Fd()))
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
// Move the container side to the container's NS
err = targetNS.Do(func(_ ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName(contVethName)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetUp(link)
Expect(err).NotTo(HaveOccurred())
return nil
})
// Start the DHCP server
dhcpServerDone, err = dhcpServerStart(originalNS, net.IPv4(192, 168, 1, 5), serverIP.IP, 1, dhcpServerStopCh)
Expect(err).NotTo(HaveOccurred())
// Start the DHCP client daemon
dhcpPluginPath, err := exec.LookPath("dhcp")
Expect(err).NotTo(HaveOccurred())
clientCmd = exec.Command(dhcpPluginPath, "daemon", "-socketpath", socketPath)
err = clientCmd.Start()
Expect(err).NotTo(HaveOccurred())
Expect(clientCmd.Process).NotTo(BeNil())
// Wait up to 15 seconds for the client socket
Eventually(func() bool {
_, err := os.Stat(socketPath)
return err == nil
}, time.Second*15, time.Second/4).Should(BeTrue())
})
AfterEach(func() {
dhcpServerStopCh <- true
dhcpServerDone.Wait()
clientCmd.Process.Kill()
clientCmd.Wait()
Expect(originalNS.Close()).To(Succeed())
Expect(targetNS.Close()).To(Succeed())
defer os.RemoveAll(tmpDir)
})
It("configures and deconfigures a link with ADD/DEL", func() {
conf := fmt.Sprintf(`{
"cniVersion": "0.3.1",
"name": "mynet",
"type": "ipvlan",
"ipam": {
"type": "dhcp",
"daemonSocketPath": "%s"
}
}`, socketPath)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: contVethName,
StdinData: []byte(conf),
}
var addResult *current.Result
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
r, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
addResult, err = current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(len(addResult.IPs)).To(Equal(1))
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24"))
return nil
})
Expect(err).NotTo(HaveOccurred())
err = originalNS.Do(func(ns.NetNS) error {
return testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
})
Expect(err).NotTo(HaveOccurred())
})
It("correctly handles multiple DELs for the same container", func() {
conf := fmt.Sprintf(`{
"cniVersion": "0.3.1",
"name": "mynet",
"type": "ipvlan",
"ipam": {
"type": "dhcp",
"daemonSocketPath": "%s"
}
}`, socketPath)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: contVethName,
StdinData: []byte(conf),
}
var addResult *current.Result
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
r, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
addResult, err = current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(len(addResult.IPs)).To(Equal(1))
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24"))
return nil
})
Expect(err).NotTo(HaveOccurred())
wg := sync.WaitGroup{}
wg.Add(3)
started := sync.WaitGroup{}
started.Add(3)
for i := 0; i < 3; i++ {
go func() {
defer GinkgoRecover()
// Wait until all goroutines are running
started.Done()
started.Wait()
err = originalNS.Do(func(ns.NetNS) error {
return testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
})
Expect(err).NotTo(HaveOccurred())
wg.Done()
}()
}
wg.Wait()
err = originalNS.Do(func(ns.NetNS) error {
return testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
})
Expect(err).NotTo(HaveOccurred())
})
})
const (
hostBridgeName string = "dhcpbr0"
hostVethName0 string = "br-eth0"
contVethName0 string = "eth0"
hostVethName1 string = "br-eth1"
contVethName1 string = "eth1"
)
func dhcpSetupOriginalNS() (chan bool, net.IPNet, string, ns.NetNS, ns.NetNS, error) {
var originalNS, targetNS ns.NetNS
var dhcpServerStopCh chan bool
var socketPath string
var br *netlink.Bridge
var tmpDir string
var err error
dhcpServerStopCh = make(chan bool)
tmpDir, err = getTmpDir()
Expect(err).NotTo(HaveOccurred())
socketPath = filepath.Join(tmpDir, "dhcp.sock")
// Create a new NetNS so we don't modify the host
originalNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
targetNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
serverIP := net.IPNet{
IP: net.IPv4(192, 168, 1, 1),
Mask: net.IPv4Mask(255, 255, 255, 0),
}
// Use (original) NS
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
// Create bridge in the "host" (original) NS
br = &netlink.Bridge{
LinkAttrs: netlink.LinkAttrs{
Name: hostBridgeName,
},
}
err = netlink.LinkAdd(br)
Expect(err).NotTo(HaveOccurred())
address := &netlink.Addr{IPNet: &net.IPNet{
IP: net.IPv4(192, 168, 1, 1),
Mask: net.IPv4Mask(255, 255, 255, 0),
}}
err = netlink.AddrAdd(br, address)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetUp(br)
Expect(err).NotTo(HaveOccurred())
err = netlink.RouteAdd(&netlink.Route{
LinkIndex: br.Attrs().Index,
Scope: netlink.SCOPE_UNIVERSE,
Dst: &net.IPNet{
IP: net.IPv4(0, 0, 0, 0),
Mask: net.IPv4Mask(0, 0, 0, 0),
},
})
Expect(err).NotTo(HaveOccurred())
// Create veth pair eth0
vethLinkAttrs := netlink.NewLinkAttrs()
vethLinkAttrs.Name = hostVethName0
veth := &netlink.Veth{
LinkAttrs: vethLinkAttrs,
PeerName: contVethName0,
}
err = netlink.LinkAdd(veth)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetUp(veth)
Expect(err).NotTo(HaveOccurred())
bridgeLink, err := netlink.LinkByName(hostBridgeName)
Expect(err).NotTo(HaveOccurred())
hostVethLink, err := netlink.LinkByName(hostVethName0)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetMaster(hostVethLink, bridgeLink.(*netlink.Bridge))
Expect(err).NotTo(HaveOccurred())
cont, err := netlink.LinkByName(contVethName0)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetNsFd(cont, int(targetNS.Fd()))
Expect(err).NotTo(HaveOccurred())
// Create veth path - eth1
vethLinkAttrs1 := netlink.NewLinkAttrs()
vethLinkAttrs1.Name = hostVethName1
veth1 := &netlink.Veth{
LinkAttrs: vethLinkAttrs1,
PeerName: contVethName1,
}
err = netlink.LinkAdd(veth1)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetUp(veth1)
Expect(err).NotTo(HaveOccurred())
bridgeLink, err = netlink.LinkByName(hostBridgeName)
Expect(err).NotTo(HaveOccurred())
hostVethLink1, err := netlink.LinkByName(hostVethName1)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetMaster(hostVethLink1, bridgeLink.(*netlink.Bridge))
Expect(err).NotTo(HaveOccurred())
cont1, err := netlink.LinkByName(contVethName1)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetNsFd(cont1, int(targetNS.Fd()))
Expect(err).NotTo(HaveOccurred())
return nil
})
return dhcpServerStopCh, serverIP, socketPath, originalNS, targetNS, err
}
var _ = Describe("DHCP Lease Unavailable Operations", func() {
var originalNS, targetNS ns.NetNS
var dhcpServerStopCh chan bool
var dhcpServerDone *sync.WaitGroup
var clientCmd *exec.Cmd
var socketPath string
var tmpDir string
var serverIP net.IPNet
var err error
BeforeEach(func() {
dhcpServerStopCh, serverIP, socketPath, originalNS, targetNS, err = dhcpSetupOriginalNS()
Expect(err).NotTo(HaveOccurred())
// Move the container side to the container's NS
err = targetNS.Do(func(_ ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName(contVethName0)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetUp(link)
Expect(err).NotTo(HaveOccurred())
link1, err := netlink.LinkByName(contVethName1)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetUp(link1)
Expect(err).NotTo(HaveOccurred())
return nil
})
// Start the DHCP server
dhcpServerDone, err = dhcpServerStart(originalNS, net.IPv4(192, 168, 1, 5), serverIP.IP, 1, dhcpServerStopCh)
Expect(err).NotTo(HaveOccurred())
// Start the DHCP client daemon
dhcpPluginPath, err := exec.LookPath("dhcp")
Expect(err).NotTo(HaveOccurred())
clientCmd = exec.Command(dhcpPluginPath, "daemon", "-socketpath", socketPath)
err = clientCmd.Start()
Expect(err).NotTo(HaveOccurred())
Expect(clientCmd.Process).NotTo(BeNil())
// Wait up to 15 seconds for the client socket
Eventually(func() bool {
_, err := os.Stat(socketPath)
return err == nil
}, time.Second*15, time.Second/4).Should(BeTrue())
})
AfterEach(func() {
dhcpServerStopCh <- true
dhcpServerDone.Wait()
clientCmd.Process.Kill()
clientCmd.Wait()
Expect(originalNS.Close()).To(Succeed())
Expect(targetNS.Close()).To(Succeed())
defer os.RemoveAll(tmpDir)
})
It("Configures multiple links with multiple ADD with second lease unavailable", func() {
conf := fmt.Sprintf(`{
"cniVersion": "0.3.1",
"name": "mynet",
"type": "bridge",
"bridge": "%s",
"ipam": {
"type": "dhcp",
"daemonSocketPath": "%s"
}
}`, hostBridgeName, socketPath)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: contVethName0,
StdinData: []byte(conf),
}
var addResult *current.Result
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
r, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
addResult, err = current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(len(addResult.IPs)).To(Equal(1))
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24"))
return nil
})
Expect(err).NotTo(HaveOccurred())
args = &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: contVethName1,
StdinData: []byte(conf),
}
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
_, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).To(HaveOccurred())
println(err.Error())
Expect(err.Error()).To(Equal("error calling DHCP.Allocate: no more tries"))
return nil
})
Expect(err).NotTo(HaveOccurred())
args = &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: contVethName1,
StdinData: []byte(conf),
}
err = originalNS.Do(func(ns.NetNS) error {
return testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
})
Expect(err).NotTo(HaveOccurred())
args = &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: contVethName0,
StdinData: []byte(conf),
}
err = originalNS.Do(func(ns.NetNS) error {
return testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
})
Expect(err).NotTo(HaveOccurred())
})
})

View File

@ -20,14 +20,15 @@ import (
"math/rand"
"net"
"sync"
"sync/atomic"
"time"
"github.com/d2g/dhcp4"
"github.com/d2g/dhcp4client"
"github.com/vishvananda/netlink"
"github.com/containernetworking/cni/pkg/ns"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/plugins/pkg/ns"
)
// RFC 2131 suggests using exponential backoff, starting with 4sec
@ -55,6 +56,7 @@ type DHCPLease struct {
renewalTime time.Time
rebindingTime time.Time
expireTime time.Time
stopping uint32
stop chan struct{}
wg sync.WaitGroup
}
@ -106,12 +108,14 @@ func AcquireLease(clientID, netns, ifName string) (*DHCPLease, error) {
// Stop terminates the background task that maintains the lease
// and issues a DHCP Release
func (l *DHCPLease) Stop() {
close(l.stop)
if atomic.CompareAndSwapUint32(&l.stopping, 0, 1) {
close(l.stop)
}
l.wg.Wait()
}
func (l *DHCPLease) acquire() error {
c, err := newDHCPClient(l.link)
c, err := newDHCPClient(l.link, l.clientID)
if err != nil {
return err
}
@ -124,8 +128,12 @@ func (l *DHCPLease) acquire() error {
}
}
opts := make(dhcp4.Options)
opts[dhcp4.OptionClientIdentifier] = []byte(l.clientID)
opts[dhcp4.OptionParameterRequestList] = []byte{byte(dhcp4.OptionRouter), byte(dhcp4.OptionSubnetMask)}
pkt, err := backoffRetry(func() (*dhcp4.Packet, error) {
ok, ack, err := c.Request()
ok, ack, err := DhcpRequest(c, opts)
switch {
case err != nil:
return nil, err
@ -234,14 +242,17 @@ func (l *DHCPLease) downIface() {
}
func (l *DHCPLease) renew() error {
c, err := newDHCPClient(l.link)
c, err := newDHCPClient(l.link, l.clientID)
if err != nil {
return err
}
defer c.Close()
opts := make(dhcp4.Options)
opts[dhcp4.OptionClientIdentifier] = []byte(l.clientID)
pkt, err := backoffRetry(func() (*dhcp4.Packet, error) {
ok, ack, err := c.Renew(*l.ack)
ok, ack, err := DhcpRenew(c, *l.ack, opts)
switch {
case err != nil:
return nil, err
@ -262,13 +273,16 @@ func (l *DHCPLease) renew() error {
func (l *DHCPLease) release() error {
log.Printf("%v: releasing lease", l.clientID)
c, err := newDHCPClient(l.link)
c, err := newDHCPClient(l.link, l.clientID)
if err != nil {
return err
}
defer c.Close()
if err = c.Release(*l.ack); err != nil {
opts := make(dhcp4.Options)
opts[dhcp4.OptionClientIdentifier] = []byte(l.clientID)
if err = DhcpRelease(c, *l.ack, opts); err != nil {
return fmt.Errorf("failed to send DHCPRELEASE")
}
@ -291,9 +305,27 @@ func (l *DHCPLease) Gateway() net.IP {
return parseRouter(l.opts)
}
func (l *DHCPLease) Routes() []types.Route {
routes := parseRoutes(l.opts)
return append(routes, parseCIDRRoutes(l.opts)...)
func (l *DHCPLease) Routes() []*types.Route {
routes := []*types.Route{}
// RFC 3442 states that if Classless Static Routes (option 121)
// exist, we ignore Static Routes (option 33) and the Router/Gateway.
opt121_routes := parseCIDRRoutes(l.opts)
if len(opt121_routes) > 0 {
return append(routes, opt121_routes...)
}
// Append Static Routes
routes = append(routes, parseRoutes(l.opts)...)
// The CNI spec says even if there is a gateway specified, we must
// add a default route in the routes section.
if gw := l.Gateway(); gw != nil {
_, defaultRoute, _ := net.ParseCIDR("0.0.0.0/0")
routes = append(routes, &types.Route{Dst: *defaultRoute, GW: gw})
}
return routes
}
// jitter returns a random value within [-span, span) range
@ -322,7 +354,7 @@ func backoffRetry(f func() (*dhcp4.Packet, error)) (*dhcp4.Packet, error) {
return nil, errNoMoreTries
}
func newDHCPClient(link netlink.Link) (*dhcp4client.Client, error) {
func newDHCPClient(link netlink.Link, clientID string) (*dhcp4client.Client, error) {
pktsock, err := dhcp4client.NewPacketSock(link.Attrs().Index)
if err != nil {
return nil, err

View File

@ -15,42 +15,115 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"log"
"net/rpc"
"os"
"path/filepath"
"github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/cni/pkg/version"
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
)
const socketPath = "/run/cni/dhcp.sock"
const defaultSocketPath = "/run/cni/dhcp.sock"
func main() {
if len(os.Args) > 1 && os.Args[1] == "daemon" {
runDaemon()
var pidfilePath string
var hostPrefix string
var socketPath string
daemonFlags := flag.NewFlagSet("daemon", flag.ExitOnError)
daemonFlags.StringVar(&pidfilePath, "pidfile", "", "optional path to write daemon PID to")
daemonFlags.StringVar(&hostPrefix, "hostprefix", "", "optional prefix to host root")
daemonFlags.StringVar(&socketPath, "socketpath", "", "optional dhcp server socketpath")
daemonFlags.Parse(os.Args[2:])
if socketPath == "" {
socketPath = defaultSocketPath
}
if err := runDaemon(pidfilePath, hostPrefix, socketPath); err != nil {
log.Printf(err.Error())
os.Exit(1)
}
} else {
skel.PluginMain(cmdAdd, cmdDel)
skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("dhcp"))
}
}
func cmdAdd(args *skel.CmdArgs) error {
result := types.Result{}
if err := rpcCall("DHCP.Allocate", args, &result); err != nil {
// Plugin must return result in same version as specified in netconf
versionDecoder := &version.ConfigDecoder{}
confVersion, err := versionDecoder.Decode(args.StdinData)
if err != nil {
return err
}
return result.Print()
result := &current.Result{}
if err := rpcCall("DHCP.Allocate", args, result); err != nil {
return err
}
return types.PrintResult(result, confVersion)
}
func cmdDel(args *skel.CmdArgs) error {
result := struct{}{}
if err := rpcCall("DHCP.Release", args, &result); err != nil {
return fmt.Errorf("error dialing DHCP daemon: %v", err)
return err
}
return nil
}
func cmdCheck(args *skel.CmdArgs) error {
// TODO: implement
//return fmt.Errorf("not implemented")
// Plugin must return result in same version as specified in netconf
versionDecoder := &version.ConfigDecoder{}
//confVersion, err := versionDecoder.Decode(args.StdinData)
_, err := versionDecoder.Decode(args.StdinData)
if err != nil {
return err
}
result := &current.Result{}
if err := rpcCall("DHCP.Allocate", args, result); err != nil {
return err
}
return nil
}
type SocketPathConf struct {
DaemonSocketPath string `json:"daemonSocketPath,omitempty"`
}
type TempNetConf struct {
IPAM SocketPathConf `json:"ipam,omitempty"`
}
func getSocketPath(stdinData []byte) (string, error) {
conf := TempNetConf{}
if err := json.Unmarshal(stdinData, &conf); err != nil {
return "", fmt.Errorf("error parsing socket path conf: %v", err)
}
if conf.IPAM.DaemonSocketPath == "" {
return defaultSocketPath, nil
}
return conf.IPAM.DaemonSocketPath, nil
}
func rpcCall(method string, args *skel.CmdArgs, result interface{}) error {
socketPath, err := getSocketPath(args.StdinData)
if err != nil {
return fmt.Errorf("error obtaining socketPath: %v", err)
}
client, err := rpc.DialHTTP("unix", socketPath)
if err != nil {
return fmt.Errorf("error dialing DHCP daemon: %v", err)

View File

@ -40,17 +40,17 @@ func classfulSubnet(sn net.IP) net.IPNet {
}
}
func parseRoutes(opts dhcp4.Options) []types.Route {
func parseRoutes(opts dhcp4.Options) []*types.Route {
// StaticRoutes format: pairs of:
// Dest = 4 bytes; Classful IP subnet
// Router = 4 bytes; IP address of router
routes := []types.Route{}
routes := []*types.Route{}
if opt, ok := opts[dhcp4.OptionStaticRoute]; ok {
for len(opt) >= 8 {
sn := opt[0:4]
r := opt[4:8]
rt := types.Route{
rt := &types.Route{
Dst: classfulSubnet(sn),
GW: r,
}
@ -62,10 +62,10 @@ func parseRoutes(opts dhcp4.Options) []types.Route {
return routes
}
func parseCIDRRoutes(opts dhcp4.Options) []types.Route {
func parseCIDRRoutes(opts dhcp4.Options) []*types.Route {
// See RFC4332 for format (http://tools.ietf.org/html/rfc3442)
routes := []types.Route{}
routes := []*types.Route{}
if opt, ok := opts[dhcp4.OptionClasslessRouteFormat]; ok {
for len(opt) >= 5 {
width := int(opt[0])
@ -89,7 +89,7 @@ func parseCIDRRoutes(opts dhcp4.Options) []types.Route {
gw := net.IP(opt[octets+1 : octets+5])
rt := types.Route{
rt := &types.Route{
Dst: net.IPNet{
IP: net.IP(sn),
Mask: net.CIDRMask(width, 32),
@ -98,7 +98,7 @@ func parseCIDRRoutes(opts dhcp4.Options) []types.Route {
}
routes = append(routes, rt)
opt = opt[octets+5 : len(opt)]
opt = opt[octets+5:]
}
}
return routes

View File

@ -22,16 +22,16 @@ import (
"github.com/d2g/dhcp4"
)
func validateRoutes(t *testing.T, routes []types.Route) {
expected := []types.Route{
types.Route{
func validateRoutes(t *testing.T, routes []*types.Route) {
expected := []*types.Route{
{
Dst: net.IPNet{
IP: net.IPv4(10, 0, 0, 0),
Mask: net.CIDRMask(8, 32),
},
GW: net.IPv4(10, 1, 2, 3),
},
types.Route{
{
Dst: net.IPNet{
IP: net.IPv4(192, 168, 1, 0),
Mask: net.CIDRMask(24, 32),

View File

@ -0,0 +1,11 @@
[Unit]
Description=CNI DHCP service
Documentation=https://github.com/containernetworking/plugins/tree/master/plugins/ipam/dhcp
After=network.target cni-dhcp.socket
Requires=cni-dhcp.socket
[Service]
ExecStart=/opt/cni/bin/dhcp daemon
[Install]
WantedBy=multi-user.target

View File

@ -0,0 +1,14 @@
[Unit]
Description=CNI DHCP service socket
Documentation=https://github.com/containernetworking/plugins/tree/master/plugins/ipam/dhcp
PartOf=cni-dhcp.service
[Socket]
ListenStream=/run/cni/dhcp.sock
SocketMode=0660
SocketUser=root
SocketGroup=root
RemoveOnStop=true
[Install]
WantedBy=sockets.target

View File

@ -1,86 +1,142 @@
# host-local IP address manager
# host-local IP address management plugin
host-local IPAM allocates IPv4 and IPv6 addresses out of a specified address range.
host-local IPAM allocates IPv4 and IPv6 addresses out of a specified address range. Optionally,
it can include a DNS configuration from a `resolv.conf` file on the host.
## Usage
## Overview
### Obtain an IP
host-local IPAM plugin allocates ip addresses out of a set of address ranges.
It stores the state locally on the host filesystem, therefore ensuring uniqueness of IP addresses on a single host.
Given the following network configuration:
The allocator can allocate multiple ranges, and supports sets of multiple (disjoint)
subnets. The allocation strategy is loosely round-robin within each range set.
```
## Example configurations
Note that the key `ranges` is a list of range sets. That is to say, the length
of the top-level array is the number of addresses returned. The second-level
array is a set of subnets to use as a pool of possible addresses.
This example configuration returns 2 IP addresses.
```json
{
"name": "default",
"ipam": {
"type": "host-local",
"subnet": "203.0.113.0/24"
}
}
```
#### Using the command line interface
```
$ export CNI_COMMAND=ADD
$ export CNI_CONTAINERID=f81d4fae-7dec-11d0-a765-00a0c91e6bf6
$ ./host-local < $conf
```
```
{
"ip4": {
"ip": "203.0.113.1/24"
}
}
```
## Backends
By default ipmanager stores IP allocations on the local filesystem using the IP address as the file name and the ID as contents. For example:
```
$ ls /var/lib/cni/networks/default
```
```
203.0.113.1 203.0.113.2
```
```
$ cat /var/lib/cni/networks/default/203.0.113.1
```
```
f81d4fae-7dec-11d0-a765-00a0c91e6bf6
```
## Configuration Files
```
{
"name": "ipv6",
"ipam": {
"type": "host-local",
"subnet": "3ffe:ffff:0:01ff::/64",
"range-start": "3ffe:ffff:0:01ff::0010",
"range-end": "3ffe:ffff:0:01ff::0020",
"routes": [
{ "dst": "3ffe:ffff:0:01ff::1/64" }
]
}
}
```
```
{
"name": "ipv4",
"ipam": {
"type": "host-local",
"subnet": "203.0.113.1/24",
"range-start": "203.0.113.10",
"range-end": "203.0.113.20",
"ranges": [
[
{
"subnet": "10.10.0.0/16",
"rangeStart": "10.10.1.20",
"rangeEnd": "10.10.3.50",
"gateway": "10.10.0.254"
},
{
"subnet": "172.16.5.0/24"
}
],
[
{
"subnet": "3ffe:ffff:0:01ff::/64",
"rangeStart": "3ffe:ffff:0:01ff::0010",
"rangeEnd": "3ffe:ffff:0:01ff::0020"
}
]
],
"routes": [
{ "dst": "203.0.113.0/24" }
]
{ "dst": "0.0.0.0/0" },
{ "dst": "192.168.0.0/16", "gw": "10.10.5.1" },
{ "dst": "3ffe:ffff:0:01ff::1/64" }
],
"dataDir": "/run/my-orchestrator/container-ipam-state"
}
}
```
Previous versions of the `host-local` allocator did not support the `ranges`
property, and instead expected a single range on the top level. This is
deprecated but still supported.
```json
{
"ipam": {
"type": "host-local",
"subnet": "3ffe:ffff:0:01ff::/64",
"rangeStart": "3ffe:ffff:0:01ff::0010",
"rangeEnd": "3ffe:ffff:0:01ff::0020",
"routes": [
{ "dst": "3ffe:ffff:0:01ff::1/64" }
],
"resolvConf": "/etc/resolv.conf"
}
}
```
We can test it out on the command-line:
```bash
$ echo '{ "cniVersion": "0.3.1", "name": "examplenet", "ipam": { "type": "host-local", "ranges": [ [{"subnet": "203.0.113.0/24"}], [{"subnet": "2001:db8:1::/64"}]], "dataDir": "/tmp/cni-example" } }' | CNI_COMMAND=ADD CNI_CONTAINERID=example CNI_NETNS=/dev/null CNI_IFNAME=dummy0 CNI_PATH=. ./host-local
```
```json
{
"ips": [
{
"version": "4",
"address": "203.0.113.2/24",
"gateway": "203.0.113.1"
},
{
"version": "6",
"address": "2001:db8:1::2/64",
"gateway": "2001:db8:1::1"
}
],
"dns": {}
}
```
## Network configuration reference
* `type` (string, required): "host-local".
* `routes` (string, optional): list of routes to add to the container namespace. Each route is a dictionary with "dst" and optional "gw" fields. If "gw" is omitted, value of "gateway" will be used.
* `resolvConf` (string, optional): Path to a `resolv.conf` on the host to parse and return as the DNS configuration
* `dataDir` (string, optional): Path to a directory to use for maintaining state, e.g. which IPs have been allocated to which containers
* `ranges`, (array, required, nonempty) an array of arrays of range objects:
* `subnet` (string, required): CIDR block to allocate out of.
* `rangeStart` (string, optional): IP inside of "subnet" from which to start allocating addresses. Defaults to ".2" IP inside of the "subnet" block.
* `rangeEnd` (string, optional): IP inside of "subnet" with which to end allocating addresses. Defaults to ".254" IP inside of the "subnet" block for ipv4, ".255" for IPv6
* `gateway` (string, optional): IP inside of "subnet" to designate as the gateway. Defaults to ".1" IP inside of the "subnet" block.
Older versions of the `host-local` plugin did not support the `ranges` array. Instead,
all the properties in the `range` object were top-level. This is still supported but deprecated.
## Supported arguments
The following [CNI_ARGS](https://github.com/containernetworking/cni/blob/master/SPEC.md#parameters) are supported:
* `ip`: request a specific IP address from a subnet.
The following [args conventions](https://github.com/containernetworking/cni/blob/master/CONVENTIONS.md) are supported:
* `ips` (array of strings): A list of custom IPs to attempt to allocate
The following [Capability Args](https://github.com/containernetworking/cni/blob/master/CONVENTIONS.md) are supported:
* `ipRanges`: The exact same as the `ranges` array - a list of address pools
### Custom IP allocation
For every requested custom IP, the `host-local` allocator will request that IP
if it falls within one of the `range` objects. Thus it is possible to specify
multiple custom IPs and multiple ranges.
If any requested IPs cannot be reserved, either because they are already in use
or are not part of a specified range, the plugin will return an error.
## Files
Allocated IP addresses are stored as files in `/var/lib/cni/networks/$NETWORK_NAME`.
The path can be customized with the `dataDir` option listed above. Environments
where IPs are released automatically on reboot (e.g. running containers are not
restored) may wish to specify `/var/run/cni` or another tmpfs mounted directory
instead.

View File

@ -1,165 +0,0 @@
// Copyright 2015 CNI authors
//
// 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.
package main
import (
"fmt"
"net"
"github.com/containernetworking/cni/pkg/ip"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/plugins/ipam/host-local/backend"
)
type IPAllocator struct {
start net.IP
end net.IP
conf *IPAMConfig
store backend.Store
}
func NewIPAllocator(conf *IPAMConfig, store backend.Store) (*IPAllocator, error) {
var (
start net.IP
end net.IP
err error
)
start, end, err = networkRange((*net.IPNet)(&conf.Subnet))
if err != nil {
return nil, err
}
// skip the .0 address
start = ip.NextIP(start)
if conf.RangeStart != nil {
if err := validateRangeIP(conf.RangeStart, (*net.IPNet)(&conf.Subnet)); err != nil {
return nil, err
}
start = conf.RangeStart
}
if conf.RangeEnd != nil {
if err := validateRangeIP(conf.RangeEnd, (*net.IPNet)(&conf.Subnet)); err != nil {
return nil, err
}
// RangeEnd is inclusive
end = ip.NextIP(conf.RangeEnd)
}
return &IPAllocator{start, end, conf, store}, nil
}
func validateRangeIP(ip net.IP, ipnet *net.IPNet) error {
if !ipnet.Contains(ip) {
return fmt.Errorf("%s not in network: %s", ip, ipnet)
}
return nil
}
// Returns newly allocated IP along with its config
func (a *IPAllocator) Get(id string) (*types.IPConfig, error) {
a.store.Lock()
defer a.store.Unlock()
gw := a.conf.Gateway
if gw == nil {
gw = ip.NextIP(a.conf.Subnet.IP)
}
var requestedIP net.IP
if a.conf.Args != nil {
requestedIP = a.conf.Args.IP
}
if requestedIP != nil {
if gw != nil && gw.Equal(a.conf.Args.IP) {
return nil, fmt.Errorf("requested IP must differ gateway IP")
}
subnet := net.IPNet{
IP: a.conf.Subnet.IP,
Mask: a.conf.Subnet.Mask,
}
err := validateRangeIP(requestedIP, &subnet)
if err != nil {
return nil, err
}
reserved, err := a.store.Reserve(id, requestedIP)
if err != nil {
return nil, err
}
if reserved {
return &types.IPConfig{
IP: net.IPNet{IP: requestedIP, Mask: a.conf.Subnet.Mask},
Gateway: gw,
Routes: a.conf.Routes,
}, nil
}
return nil, fmt.Errorf("requested IP address %q is not available in network: %s", requestedIP, a.conf.Name)
}
for cur := a.start; !cur.Equal(a.end); cur = ip.NextIP(cur) {
// don't allocate gateway IP
if gw != nil && cur.Equal(gw) {
continue
}
reserved, err := a.store.Reserve(id, cur)
if err != nil {
return nil, err
}
if reserved {
return &types.IPConfig{
IP: net.IPNet{IP: cur, Mask: a.conf.Subnet.Mask},
Gateway: gw,
Routes: a.conf.Routes,
}, nil
}
}
return nil, fmt.Errorf("no IP addresses available in network: %s", a.conf.Name)
}
// Releases all IPs allocated for the container with given ID
func (a *IPAllocator) Release(id string) error {
a.store.Lock()
defer a.store.Unlock()
return a.store.ReleaseByID(id)
}
func networkRange(ipnet *net.IPNet) (net.IP, net.IP, error) {
if ipnet.IP == nil {
return nil, nil, fmt.Errorf("missing field %q in IPAM configuration", "subnet")
}
ip := ipnet.IP.To4()
if ip == nil {
ip = ipnet.IP.To16()
if ip == nil {
return nil, nil, fmt.Errorf("IP not v4 nor v6")
}
}
if len(ip) != len(ipnet.Mask) {
return nil, nil, fmt.Errorf("IPNet IP and Mask version mismatch")
}
var end net.IP
for i := 0; i < len(ip); i++ {
end = append(end, ip[i]|^ipnet.Mask[i])
}
return ipnet.IP, end, nil
}

View File

@ -0,0 +1,228 @@
// Copyright 2015 CNI authors
//
// 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.
package allocator
import (
"fmt"
"log"
"net"
"os"
"strconv"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/plugins/pkg/ip"
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend"
)
type IPAllocator struct {
rangeset *RangeSet
store backend.Store
rangeID string // Used for tracking last reserved ip
}
func NewIPAllocator(s *RangeSet, store backend.Store, id int) *IPAllocator {
return &IPAllocator{
rangeset: s,
store: store,
rangeID: strconv.Itoa(id),
}
}
// Get allocates an IP
func (a *IPAllocator) Get(id string, ifname string, requestedIP net.IP) (*current.IPConfig, error) {
a.store.Lock()
defer a.store.Unlock()
var reservedIP *net.IPNet
var gw net.IP
if requestedIP != nil {
if err := canonicalizeIP(&requestedIP); err != nil {
return nil, err
}
r, err := a.rangeset.RangeFor(requestedIP)
if err != nil {
return nil, err
}
if requestedIP.Equal(r.Gateway) {
return nil, fmt.Errorf("requested ip %s is subnet's gateway", requestedIP.String())
}
reserved, err := a.store.Reserve(id, ifname, requestedIP, a.rangeID)
if err != nil {
return nil, err
}
if !reserved {
return nil, fmt.Errorf("requested IP address %s is not available in range set %s", requestedIP, a.rangeset.String())
}
reservedIP = &net.IPNet{IP: requestedIP, Mask: r.Subnet.Mask}
gw = r.Gateway
} else {
// try to get allocated IPs for this given id, if exists, just return error
// because duplicate allocation is not allowed in SPEC
// https://github.com/containernetworking/cni/blob/master/SPEC.md
allocatedIPs := a.store.GetByID(id, ifname)
for _, allocatedIP := range allocatedIPs {
// check whether the existing IP belong to this range set
if _, err := a.rangeset.RangeFor(allocatedIP); err == nil {
return nil, fmt.Errorf("%s has been allocated to %s, duplicate allocation is not allowed", allocatedIP.String(), id)
}
}
iter, err := a.GetIter()
if err != nil {
return nil, err
}
for {
reservedIP, gw = iter.Next()
if reservedIP == nil {
break
}
reserved, err := a.store.Reserve(id, ifname, reservedIP.IP, a.rangeID)
if err != nil {
return nil, err
}
if reserved {
break
}
}
}
if reservedIP == nil {
return nil, fmt.Errorf("no IP addresses available in range set: %s", a.rangeset.String())
}
version := "4"
if reservedIP.IP.To4() == nil {
version = "6"
}
return &current.IPConfig{
Version: version,
Address: *reservedIP,
Gateway: gw,
}, nil
}
// Release clears all IPs allocated for the container with given ID
func (a *IPAllocator) Release(id string, ifname string) error {
a.store.Lock()
defer a.store.Unlock()
return a.store.ReleaseByID(id, ifname)
}
type RangeIter struct {
rangeset *RangeSet
// The current range id
rangeIdx int
// Our current position
cur net.IP
// The IP and range index where we started iterating; if we hit this again, we're done.
startIP net.IP
startRange int
}
// GetIter encapsulates the strategy for this allocator.
// We use a round-robin strategy, attempting to evenly use the whole set.
// More specifically, a crash-looping container will not see the same IP until
// the entire range has been run through.
// We may wish to consider avoiding recently-released IPs in the future.
func (a *IPAllocator) GetIter() (*RangeIter, error) {
iter := RangeIter{
rangeset: a.rangeset,
}
// Round-robin by trying to allocate from the last reserved IP + 1
startFromLastReservedIP := false
// We might get a last reserved IP that is wrong if the range indexes changed.
// This is not critical, we just lose round-robin this one time.
lastReservedIP, err := a.store.LastReservedIP(a.rangeID)
if err != nil && !os.IsNotExist(err) {
log.Printf("Error retrieving last reserved ip: %v", err)
} else if lastReservedIP != nil {
startFromLastReservedIP = a.rangeset.Contains(lastReservedIP)
}
// Find the range in the set with this IP
if startFromLastReservedIP {
for i, r := range *a.rangeset {
if r.Contains(lastReservedIP) {
iter.rangeIdx = i
iter.startRange = i
// We advance the cursor on every Next(), so the first call
// to next() will return lastReservedIP + 1
iter.cur = lastReservedIP
break
}
}
} else {
iter.rangeIdx = 0
iter.startRange = 0
iter.startIP = (*a.rangeset)[0].RangeStart
}
return &iter, nil
}
// Next returns the next IP, its mask, and its gateway. Returns nil
// if the iterator has been exhausted
func (i *RangeIter) Next() (*net.IPNet, net.IP) {
r := (*i.rangeset)[i.rangeIdx]
// If this is the first time iterating and we're not starting in the middle
// of the range, then start at rangeStart, which is inclusive
if i.cur == nil {
i.cur = r.RangeStart
i.startIP = i.cur
if i.cur.Equal(r.Gateway) {
return i.Next()
}
return &net.IPNet{IP: i.cur, Mask: r.Subnet.Mask}, r.Gateway
}
// If we've reached the end of this range, we need to advance the range
// RangeEnd is inclusive as well
if i.cur.Equal(r.RangeEnd) {
i.rangeIdx += 1
i.rangeIdx %= len(*i.rangeset)
r = (*i.rangeset)[i.rangeIdx]
i.cur = r.RangeStart
} else {
i.cur = ip.NextIP(i.cur)
}
if i.startIP == nil {
i.startIP = i.cur
} else if i.rangeIdx == i.startRange && i.cur.Equal(i.startIP) {
// IF we've looped back to where we started, give up
return nil, nil
}
if i.cur.Equal(r.Gateway) {
return i.Next()
}
return &net.IPNet{IP: i.cur, Mask: r.Subnet.Mask}, r.Gateway
}

View File

@ -0,0 +1,27 @@
// Copyright 2016 CNI authors
//
// 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.
package allocator_test
import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"testing"
)
func TestAllocator(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "plugins/ipam/host-local/backend/allocator")
}

View File

@ -0,0 +1,333 @@
// Copyright 2017 CNI authors
//
// 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.
package allocator
import (
"fmt"
"net"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
fakestore "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
type AllocatorTestCase struct {
subnets []string
ipmap map[string]string
expectResult string
lastIP string
}
func mkalloc() IPAllocator {
p := RangeSet{
Range{Subnet: mustSubnet("192.168.1.0/29")},
}
p.Canonicalize()
store := fakestore.NewFakeStore(map[string]string{}, map[string]net.IP{})
alloc := IPAllocator{
rangeset: &p,
store: store,
rangeID: "rangeid",
}
return alloc
}
func (t AllocatorTestCase) run(idx int) (*current.IPConfig, error) {
fmt.Fprintln(GinkgoWriter, "Index:", idx)
p := RangeSet{}
for _, s := range t.subnets {
subnet, err := types.ParseCIDR(s)
if err != nil {
return nil, err
}
p = append(p, Range{Subnet: types.IPNet(*subnet)})
}
Expect(p.Canonicalize()).To(BeNil())
store := fakestore.NewFakeStore(t.ipmap, map[string]net.IP{"rangeid": net.ParseIP(t.lastIP)})
alloc := IPAllocator{
rangeset: &p,
store: store,
rangeID: "rangeid",
}
return alloc.Get("ID", "eth0", nil)
}
var _ = Describe("host-local ip allocator", func() {
Context("RangeIter", func() {
It("should loop correctly from the beginning", func() {
a := mkalloc()
r, _ := a.GetIter()
Expect(r.nextip()).To(Equal(net.IP{192, 168, 1, 2}))
Expect(r.nextip()).To(Equal(net.IP{192, 168, 1, 3}))
Expect(r.nextip()).To(Equal(net.IP{192, 168, 1, 4}))
Expect(r.nextip()).To(Equal(net.IP{192, 168, 1, 5}))
Expect(r.nextip()).To(Equal(net.IP{192, 168, 1, 6}))
Expect(r.nextip()).To(BeNil())
})
It("should loop correctly from the end", func() {
a := mkalloc()
a.store.Reserve("ID", "eth0", net.IP{192, 168, 1, 6}, a.rangeID)
a.store.ReleaseByID("ID", "eth0")
r, _ := a.GetIter()
Expect(r.nextip()).To(Equal(net.IP{192, 168, 1, 2}))
Expect(r.nextip()).To(Equal(net.IP{192, 168, 1, 3}))
Expect(r.nextip()).To(Equal(net.IP{192, 168, 1, 4}))
Expect(r.nextip()).To(Equal(net.IP{192, 168, 1, 5}))
Expect(r.nextip()).To(Equal(net.IP{192, 168, 1, 6}))
Expect(r.nextip()).To(BeNil())
})
It("should loop correctly from the middle", func() {
a := mkalloc()
a.store.Reserve("ID", "eth0", net.IP{192, 168, 1, 3}, a.rangeID)
a.store.ReleaseByID("ID", "eth0")
r, _ := a.GetIter()
Expect(r.nextip()).To(Equal(net.IP{192, 168, 1, 4}))
Expect(r.nextip()).To(Equal(net.IP{192, 168, 1, 5}))
Expect(r.nextip()).To(Equal(net.IP{192, 168, 1, 6}))
Expect(r.nextip()).To(Equal(net.IP{192, 168, 1, 2}))
Expect(r.nextip()).To(Equal(net.IP{192, 168, 1, 3}))
Expect(r.nextip()).To(BeNil())
})
})
Context("when has free ip", func() {
It("should allocate ips in round robin", func() {
testCases := []AllocatorTestCase{
// fresh start
{
subnets: []string{"10.0.0.0/29"},
ipmap: map[string]string{},
expectResult: "10.0.0.2",
lastIP: "",
},
{
subnets: []string{"2001:db8:1::0/64"},
ipmap: map[string]string{},
expectResult: "2001:db8:1::2",
lastIP: "",
},
{
subnets: []string{"10.0.0.0/30"},
ipmap: map[string]string{},
expectResult: "10.0.0.2",
lastIP: "",
},
{
subnets: []string{"10.0.0.0/29"},
ipmap: map[string]string{
"10.0.0.2": "id",
},
expectResult: "10.0.0.3",
lastIP: "",
},
// next ip of last reserved ip
{
subnets: []string{"10.0.0.0/29"},
ipmap: map[string]string{},
expectResult: "10.0.0.6",
lastIP: "10.0.0.5",
},
{
subnets: []string{"10.0.0.0/29"},
ipmap: map[string]string{
"10.0.0.4": "id",
"10.0.0.5": "id",
},
expectResult: "10.0.0.6",
lastIP: "10.0.0.3",
},
// round robin to the beginning
{
subnets: []string{"10.0.0.0/29"},
ipmap: map[string]string{
"10.0.0.6": "id",
},
expectResult: "10.0.0.2",
lastIP: "10.0.0.5",
},
// lastIP is out of range
{
subnets: []string{"10.0.0.0/29"},
ipmap: map[string]string{
"10.0.0.2": "id",
},
expectResult: "10.0.0.3",
lastIP: "10.0.0.128",
},
// subnet is completely full except for lastip
// wrap around and reserve lastIP
{
subnets: []string{"10.0.0.0/29"},
ipmap: map[string]string{
"10.0.0.2": "id",
"10.0.0.4": "id",
"10.0.0.5": "id",
"10.0.0.6": "id",
},
expectResult: "10.0.0.3",
lastIP: "10.0.0.3",
},
// alocate from multiple subnets
{
subnets: []string{"10.0.0.0/30", "10.0.1.0/30"},
expectResult: "10.0.0.2",
ipmap: map[string]string{},
},
// advance to next subnet
{
subnets: []string{"10.0.0.0/30", "10.0.1.0/30"},
lastIP: "10.0.0.2",
expectResult: "10.0.1.2",
ipmap: map[string]string{},
},
// Roll to start subnet
{
subnets: []string{"10.0.0.0/30", "10.0.1.0/30", "10.0.2.0/30"},
lastIP: "10.0.2.2",
expectResult: "10.0.0.2",
ipmap: map[string]string{},
},
}
for idx, tc := range testCases {
res, err := tc.run(idx)
Expect(err).ToNot(HaveOccurred())
Expect(res.Address.IP.String()).To(Equal(tc.expectResult))
}
})
It("should not allocate the broadcast address", func() {
alloc := mkalloc()
for i := 2; i < 7; i++ {
res, err := alloc.Get(fmt.Sprintf("ID%d", i), "eth0", nil)
Expect(err).ToNot(HaveOccurred())
s := fmt.Sprintf("192.168.1.%d/29", i)
Expect(s).To(Equal(res.Address.String()))
fmt.Fprintln(GinkgoWriter, "got ip", res.Address.String())
}
x, err := alloc.Get("ID8", "eth0", nil)
fmt.Fprintln(GinkgoWriter, "got ip", x)
Expect(err).To(HaveOccurred())
})
It("should allocate in a round-robin fashion", func() {
alloc := mkalloc()
res, err := alloc.Get("ID", "eth0", nil)
Expect(err).ToNot(HaveOccurred())
Expect(res.Address.String()).To(Equal("192.168.1.2/29"))
err = alloc.Release("ID", "eth0")
Expect(err).ToNot(HaveOccurred())
res, err = alloc.Get("ID", "eth0", nil)
Expect(err).ToNot(HaveOccurred())
Expect(res.Address.String()).To(Equal("192.168.1.3/29"))
})
Context("when requesting a specific IP", func() {
It("must allocate the requested IP", func() {
alloc := mkalloc()
requestedIP := net.IP{192, 168, 1, 5}
res, err := alloc.Get("ID", "eth0", requestedIP)
Expect(err).ToNot(HaveOccurred())
Expect(res.Address.IP.String()).To(Equal(requestedIP.String()))
})
It("must fail when the requested IP is allocated", func() {
alloc := mkalloc()
requestedIP := net.IP{192, 168, 1, 5}
res, err := alloc.Get("ID", "eth0", requestedIP)
Expect(err).ToNot(HaveOccurred())
Expect(res.Address.IP.String()).To(Equal(requestedIP.String()))
_, err = alloc.Get("ID", "eth0", requestedIP)
Expect(err).To(MatchError(`requested IP address 192.168.1.5 is not available in range set 192.168.1.1-192.168.1.6`))
})
It("must return an error when the requested IP is after RangeEnd", func() {
alloc := mkalloc()
(*alloc.rangeset)[0].RangeEnd = net.IP{192, 168, 1, 4}
requestedIP := net.IP{192, 168, 1, 5}
_, err := alloc.Get("ID", "eth0", requestedIP)
Expect(err).To(HaveOccurred())
})
It("must return an error when the requested IP is before RangeStart", func() {
alloc := mkalloc()
(*alloc.rangeset)[0].RangeStart = net.IP{192, 168, 1, 3}
requestedIP := net.IP{192, 168, 1, 2}
_, err := alloc.Get("ID", "eth0", requestedIP)
Expect(err).To(HaveOccurred())
})
})
})
Context("when out of ips", func() {
It("returns a meaningful error", func() {
testCases := []AllocatorTestCase{
{
subnets: []string{"10.0.0.0/30"},
ipmap: map[string]string{
"10.0.0.2": "id",
},
},
{
subnets: []string{"10.0.0.0/29"},
ipmap: map[string]string{
"10.0.0.2": "id",
"10.0.0.3": "id",
"10.0.0.4": "id",
"10.0.0.5": "id",
"10.0.0.6": "id",
},
},
{
subnets: []string{"10.0.0.0/30", "10.0.1.0/30"},
ipmap: map[string]string{
"10.0.0.2": "id",
"10.0.1.2": "id",
},
},
}
for idx, tc := range testCases {
_, err := tc.run(idx)
Expect(err).NotTo(BeNil())
Expect(err.Error()).To(HavePrefix("no IP addresses available in range set"))
}
})
})
})
// nextip is a convenience function used for testing
func (i *RangeIter) nextip() net.IP {
c, _ := i.Next()
if c == nil {
return nil
}
return c.IP
}

View File

@ -0,0 +1,160 @@
// Copyright 2015 CNI authors
//
// 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.
package allocator
import (
"encoding/json"
"fmt"
"net"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/020"
)
// The top-level network config - IPAM plugins are passed the full configuration
// of the calling plugin, not just the IPAM section.
type Net struct {
Name string `json:"name"`
CNIVersion string `json:"cniVersion"`
IPAM *IPAMConfig `json:"ipam"`
RuntimeConfig struct { // The capability arg
IPRanges []RangeSet `json:"ipRanges,omitempty"`
} `json:"runtimeConfig,omitempty"`
Args *struct {
A *IPAMArgs `json:"cni"`
} `json:"args"`
}
// IPAMConfig represents the IP related network configuration.
// This nests Range because we initially only supported a single
// range directly, and wish to preserve backwards compatability
type IPAMConfig struct {
*Range
Name string
Type string `json:"type"`
Routes []*types.Route `json:"routes"`
DataDir string `json:"dataDir"`
ResolvConf string `json:"resolvConf"`
Ranges []RangeSet `json:"ranges"`
IPArgs []net.IP `json:"-"` // Requested IPs from CNI_ARGS and args
}
type IPAMEnvArgs struct {
types.CommonArgs
IP net.IP `json:"ip,omitempty"`
}
type IPAMArgs struct {
IPs []net.IP `json:"ips"`
}
type RangeSet []Range
type Range struct {
RangeStart net.IP `json:"rangeStart,omitempty"` // The first ip, inclusive
RangeEnd net.IP `json:"rangeEnd,omitempty"` // The last ip, inclusive
Subnet types.IPNet `json:"subnet"`
Gateway net.IP `json:"gateway,omitempty"`
}
// NewIPAMConfig creates a NetworkConfig from the given network name.
func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) {
n := Net{}
if err := json.Unmarshal(bytes, &n); err != nil {
return nil, "", err
}
if n.IPAM == nil {
return nil, "", fmt.Errorf("IPAM config missing 'ipam' key")
}
// Parse custom IP from both env args *and* the top-level args config
if envArgs != "" {
e := IPAMEnvArgs{}
err := types.LoadArgs(envArgs, &e)
if err != nil {
return nil, "", err
}
if e.IP != nil {
n.IPAM.IPArgs = []net.IP{e.IP}
}
}
if n.Args != nil && n.Args.A != nil && len(n.Args.A.IPs) != 0 {
n.IPAM.IPArgs = append(n.IPAM.IPArgs, n.Args.A.IPs...)
}
for idx := range n.IPAM.IPArgs {
if err := canonicalizeIP(&n.IPAM.IPArgs[idx]); err != nil {
return nil, "", fmt.Errorf("cannot understand ip: %v", err)
}
}
// If a single range (old-style config) is specified, prepend it to
// the Ranges array
if n.IPAM.Range != nil && n.IPAM.Range.Subnet.IP != nil {
n.IPAM.Ranges = append([]RangeSet{{*n.IPAM.Range}}, n.IPAM.Ranges...)
}
n.IPAM.Range = nil
// If a range is supplied as a runtime config, prepend it to the Ranges
if len(n.RuntimeConfig.IPRanges) > 0 {
n.IPAM.Ranges = append(n.RuntimeConfig.IPRanges, n.IPAM.Ranges...)
}
if len(n.IPAM.Ranges) == 0 {
return nil, "", fmt.Errorf("no IP ranges specified")
}
// Validate all ranges
numV4 := 0
numV6 := 0
for i := range n.IPAM.Ranges {
if err := n.IPAM.Ranges[i].Canonicalize(); err != nil {
return nil, "", fmt.Errorf("invalid range set %d: %s", i, err)
}
if n.IPAM.Ranges[i][0].RangeStart.To4() != nil {
numV4++
} else {
numV6++
}
}
// CNI spec 0.2.0 and below supported only one v4 and v6 address
if numV4 > 1 || numV6 > 1 {
for _, v := range types020.SupportedVersions {
if n.CNIVersion == v {
return nil, "", fmt.Errorf("CNI version %v does not support more than 1 address per family", n.CNIVersion)
}
}
}
// Check for overlaps
l := len(n.IPAM.Ranges)
for i, p1 := range n.IPAM.Ranges[:l-1] {
for j, p2 := range n.IPAM.Ranges[i+1:] {
if p1.Overlaps(&p2) {
return nil, "", fmt.Errorf("range set %d overlaps with %d", i, (i + j + 1))
}
}
}
// Copy net name into IPAM so not to drag Net struct around
n.IPAM.Name = n.Name
return n.IPAM, n.CNIVersion, nil
}

Some files were not shown because too many files have changed in this diff Show More