From 405136e8aca6030a02db74e3762f05dac51b30aa Mon Sep 17 00:00:00 2001 From: Brandon Liles Date: Thu, 15 Aug 2024 13:42:08 -0400 Subject: [PATCH] chore: support connecting to docker desktop containers on windows --- go.mod | 52 ++- go.sum | 49 +++ main.go => main_darwin.go | 7 +- main_windows.go | 345 ++++++++++++++++++ ...orkmanager.go => networkmanager_darwin.go} | 2 + networkmanager/networkmanager_windows.go | 100 +++++ 6 files changed, 537 insertions(+), 18 deletions(-) rename main.go => main_darwin.go (96%) create mode 100644 main_windows.go rename networkmanager/{networkmanager.go => networkmanager_darwin.go} (97%) create mode 100644 networkmanager/networkmanager_windows.go diff --git a/go.mod b/go.mod index 37eb825..63b4bff 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,16 @@ module github.com/chipmk/docker-mac-net-connect -go 1.17 +go 1.21 + +toolchain go1.22.5 + +require ( + github.com/docker/docker v27.1.1+incompatible + golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b + golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 +) + +require golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect require ( github.com/Microsoft/go-winio v0.4.17 // indirect @@ -12,22 +22,28 @@ require ( github.com/containerd/fifo v1.0.0 // indirect github.com/containerd/ttrpc v1.1.0 // indirect github.com/containerd/typeurl v1.0.2 // indirect + github.com/distribution/reference v0.6.0 // indirect github.com/docker/distribution v2.7.1+incompatible // indirect - github.com/docker/docker v20.10.10+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect github.com/docker/go-units v0.4.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/gogo/googleapis v1.4.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/golang/protobuf v1.5.0 // indirect - github.com/google/go-cmp v0.5.6 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/uuid v1.2.0 // indirect - github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 // indirect + github.com/josharian/native v1.1.0 // indirect github.com/klauspost/compress v1.11.13 // indirect - github.com/mdlayher/genetlink v1.0.0 // indirect - github.com/mdlayher/netlink v1.4.1 // indirect - github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00 // indirect + github.com/lxn/walk v0.0.0-20210112085537-c389da54e794 + github.com/lxn/win v0.0.0-20210218163916-a377121e959e + github.com/mdlayher/genetlink v1.3.2 // indirect + github.com/mdlayher/netlink v1.7.2 // indirect + github.com/mdlayher/socket v0.4.1 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/locker v1.0.1 // indirect github.com/moby/sys/mountinfo v0.4.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect @@ -38,16 +54,22 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/sirupsen/logrus v1.8.1 // indirect go.opencensus.io v0.22.3 // indirect - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect - golang.org/x/net v0.0.0-20211105192438-b53810dc28af // indirect - golang.org/x/sync v0.0.0-20201207232520-09787c993a3a // indirect - golang.org/x/sys v0.0.0-20211106132015-ebca88c72f68 // indirect - golang.org/x/text v0.3.6 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + golang.org/x/crypto v0.8.0 // indirect + golang.org/x/net v0.9.0 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.7.0 // indirect + golang.org/x/text v0.9.0 // indirect golang.zx2c4.com/go118/netip v0.0.0-20211106132939-9d41d90554dd // indirect - golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect - golang.zx2c4.com/wireguard v0.0.0-20211106133050-23d4e52ac97f // indirect - golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211027115401-c9b1ec1aa6d8 // indirect google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a // indirect google.golang.org/grpc v1.42.0 // indirect google.golang.org/protobuf v1.27.1 // indirect ) + +replace ( + github.com/lxn/walk => golang.zx2c4.com/wireguard/windows v0.0.0-20210121140954-e7fc19d483bd + github.com/lxn/win => golang.zx2c4.com/wireguard/windows v0.0.0-20210224134948-620c54ef6199 +) diff --git a/go.sum b/go.sum index 82e21df..e0270b2 100644 --- a/go.sum +++ b/go.sum @@ -231,6 +231,8 @@ github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8l github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= @@ -238,6 +240,8 @@ github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BU github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v20.10.10+incompatible h1:GKkP0T7U4ks6X3lmmHKC2QDprnpRJor2Z5a8m62R9ZM= github.com/docker/docker v20.10.10+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= +github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= @@ -263,6 +267,8 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go. github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -281,6 +287,11 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= @@ -345,6 +356,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -396,6 +409,8 @@ github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA= github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= +github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= +github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= @@ -442,6 +457,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182aff github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo= github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0= github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc= +github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy56gw= +github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= @@ -453,14 +470,20 @@ github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuri github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8= github.com/mdlayher/netlink v1.4.1 h1:I154BCU+mKlIf7BgcAJB2r7QjveNPty6uNY1g9ChVfI= github.com/mdlayher/netlink v1.4.1/go.mod h1:e4/KuJ+s8UhfUpO9z00/fDZZmhSrs+oxyqAS9cNgn6Q= +github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= +github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00 h1:qEtkL8n1DAHpi5/AOgAckwGQUlMe4+jhL/GMt+GKIks= github.com/mdlayher/socket v0.0.0-20210307095302-262dc9984e00/go.mod h1:GAFlyu4/XV68LkQKYzKhIo/WW7j3Zi0YRAz/BOoanUc= +github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= +github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= @@ -647,6 +670,14 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -667,6 +698,8 @@ golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -740,6 +773,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210927181540-4e4d966f7476/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211105192438-b53810dc28af h1:SMeNJG/vclJ5wyBBd4xupMsSJIHTd1coW9g7q6KOjmY= golang.org/x/net v0.0.0-20211105192438-b53810dc28af/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -755,6 +790,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -810,6 +847,7 @@ golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -830,6 +868,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211106132015-ebca88c72f68 h1:Ywe/f3fNleF8I6F6qv3MeFoSZ6CTf2zBMMa/7qVML8M= golang.org/x/sys v0.0.0-20211106132015-ebca88c72f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -839,6 +879,7 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -889,11 +930,19 @@ golang.zx2c4.com/go118/netip v0.0.0-20211106132939-9d41d90554dd h1:gUHae7sCd+tFJ golang.zx2c4.com/go118/netip v0.0.0-20211106132939-9d41d90554dd/go.mod h1:5yyfuiqVIJ7t+3MqrpTQ+QqRkMWiESiyDvPNvKYCecg= golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY= golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= +golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg= +golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI= golang.zx2c4.com/wireguard v0.0.0-20210927201915-bb745b2ea326/go.mod h1:SDoazCvdy7RDjBPNEMBwrXhomlmtG7svs8mgwWEqtVI= golang.zx2c4.com/wireguard v0.0.0-20211106133050-23d4e52ac97f h1:y7Fit0Bj/3wSAyMJnjxsrXExB4syhxjOsg7lO3s8fuw= golang.zx2c4.com/wireguard v0.0.0-20211106133050-23d4e52ac97f/go.mod h1:M4ViMFJSFA3E7DEM9lS9NsCbDhurQdVjipAqa06Jy2Q= +golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b h1:J1CaxgLerRR5lgx3wnr6L04cJFbWoceSK9JWBdglINo= +golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b/go.mod h1:tqur9LnfstdR9ep2LaJT4lFUl0EjlHtge+gAjmsHUG4= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211027115401-c9b1ec1aa6d8 h1:5Qw4mAZBeNAX5ubJtVvzUmUJ/Zsl7wzwXRz8MrjYJaY= golang.zx2c4.com/wireguard/wgctrl v0.0.0-20211027115401-c9b1ec1aa6d8/go.mod h1:G0zJhHaavrPDNb/ygHzf4uju6nSlKMi4f1E5RCT3WpE= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 h1:CawjfCvYQH2OU3/TnxLx97WDSUDRABfT18pCOYwc2GE= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6/go.mod h1:3rxYc4HtVcSG9gVaTs2GEBdehh+sYPOwKtyUWEOTb80= +golang.zx2c4.com/wireguard/windows v0.0.0-20210121140954-e7fc19d483bd/go.mod h1:Y+FYqVFaQO6a+1uigm0N0GiuaZrLEaBxEiJ8tfH9sMQ= +golang.zx2c4.com/wireguard/windows v0.0.0-20210224134948-620c54ef6199/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= diff --git a/main.go b/main_darwin.go similarity index 96% rename from main.go rename to main_darwin.go index 3a3ec52..2dc0d74 100644 --- a/main.go +++ b/main_darwin.go @@ -16,6 +16,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/image" "github.com/docker/docker/client" "github.com/docker/docker/pkg/stdcopy" "golang.zx2c4.com/wireguard/conn" @@ -283,7 +284,7 @@ func setupVm( if err != nil { fmt.Printf("Image doesn't exist locally. Pulling...\n") - pullStream, err := dockerCli.ImagePull(ctx, imageName, types.ImagePullOptions{}) + pullStream, err := dockerCli.ImagePull(ctx, imageName, image.PullOptions{}) if err != nil { return fmt.Errorf("failed to pull setup image: %w", err) } @@ -310,13 +311,13 @@ func setupVm( } // Run container to completion - err = dockerCli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}) + err = dockerCli.ContainerStart(ctx, resp.ID, container.StartOptions{}) if err != nil { return fmt.Errorf("failed to start container: %w", err) } func() error { - reader, err := dockerCli.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{ + reader, err := dockerCli.ContainerLogs(ctx, resp.ID, container.LogsOptions{ ShowStdout: true, ShowStderr: true, Follow: true, diff --git a/main_windows.go b/main_windows.go new file mode 100644 index 0000000..98c3168 --- /dev/null +++ b/main_windows.go @@ -0,0 +1,345 @@ +//go:build windows + +package main + +import ( + "context" + "fmt" + "io" + "net" + "os" + "os/signal" + "strconv" + "syscall" + "time" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/image" + "github.com/docker/docker/client" + "github.com/docker/docker/pkg/stdcopy" + "golang.zx2c4.com/wireguard/conn" + "golang.zx2c4.com/wireguard/device" + "golang.zx2c4.com/wireguard/ipc" + "golang.zx2c4.com/wireguard/tun" + "golang.zx2c4.com/wireguard/wgctrl" + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" + + "github.com/chipmk/docker-mac-net-connect/networkmanager" + "github.com/chipmk/docker-mac-net-connect/version" +) + +const ( + ExitSetupSuccess = 0 + ExitSetupFailed = 1 +) + +const ( + ENV_WG_TUN_FD = "WG_TUN_FD" + ENV_WG_UAPI_FD = "WG_UAPI_FD" + ENV_WG_PROCESS_FOREGROUND = "WG_PROCESS_FOREGROUND" +) + +func main() { + logLevel := func() int { + switch os.Getenv("LOG_LEVEL") { + case "verbose", "debug": + return device.LogLevelVerbose + case "error": + return device.LogLevelError + case "silent": + return device.LogLevelSilent + } + return device.LogLevelVerbose + }() + + fmt.Printf("docker-mac-net-connect version '%s'\n", version.Version) + + tun, err := tun.CreateTUN("utun", device.DefaultMTU) + if err != nil { + fmt.Printf("Failed to create TUN device: %v", err) + os.Exit(ExitSetupFailed) + } + + interfaceName, err := tun.Name() + if err != nil { + fmt.Printf("Failed to get TUN device name: %v", err) + os.Exit(ExitSetupFailed) + } + + logger := device.NewLogger( + logLevel, + fmt.Sprintf("(%s) ", interfaceName), + ) + + if err != nil { + logger.Errorf("UAPI listen error: %v", err) + os.Exit(ExitSetupFailed) + } + + device := device.NewDevice(tun, conn.NewDefaultBind(), logger) + err = device.Up() + if err != nil { + logger.Errorf("Failed to listen on uapi socker: %v", err) + os.Exit(ExitSetupFailed) + } + + logger.Verbosef("Device started") + + errs := make(chan error) + term := make(chan os.Signal, 1) + + uapi, err := ipc.UAPIListen(interfaceName) + if err != nil { + logger.Errorf("Failed to listen on UAPI socket: %v", err) + os.Exit(ExitSetupFailed) + } + + go func() { + for { + conn, err := uapi.Accept() + if err != nil { + errs <- err + return + } + go device.IpcHandle(conn) + } + }() + + logger.Verbosef("UAPI listener started") + + // Wireguard configuration + + hostPeerIp := "10.33.33.1" + vmPeerIp := "10.33.33.2" + + c, err := wgctrl.New() + if err != nil { + logger.Errorf("Failed to create new wgctrl client: %v", err) + os.Exit(ExitSetupFailed) + } + + defer c.Close() + + hostPrivateKey, err := wgtypes.GeneratePrivateKey() + if err != nil { + logger.Errorf("Failed to generate host private key: %v", err) + os.Exit(ExitSetupFailed) + } + + vmPrivateKey, err := wgtypes.GeneratePrivateKey() + if err != nil { + logger.Errorf("Failed to generate VM private key: %v", err) + os.Exit(ExitSetupFailed) + } + + _, wildcardIpNet, err := net.ParseCIDR("0.0.0.0/0") + if err != nil { + logger.Errorf("Failed to parse wildcard CIDR: %v", err) + os.Exit(ExitSetupFailed) + } + + _, vmIpNet, err := net.ParseCIDR(vmPeerIp + "/32") + if err != nil { + logger.Errorf("Failed to parse VM peer CIDR: %v", err) + os.Exit(ExitSetupFailed) + } + + peer := wgtypes.PeerConfig{ + PublicKey: vmPrivateKey.PublicKey(), + AllowedIPs: []net.IPNet{ + *wildcardIpNet, + *vmIpNet, + }, + } + + port := 3333 + err = c.ConfigureDevice(interfaceName, wgtypes.Config{ + ListenPort: &port, + PrivateKey: &hostPrivateKey, + Peers: []wgtypes.PeerConfig{peer}, + }) + if err != nil { + logger.Errorf("Failed to configure Wireguard device: %v\n", err) + os.Exit(ExitSetupFailed) + } + + networkManager := networkmanager.New() + + _, stderr, err := networkManager.SetInterfaceAddress(hostPeerIp, vmPeerIp, interfaceName) + if err != nil { + logger.Errorf("Failed to set interface address: %v. %v", err, stderr) + os.Exit(ExitSetupFailed) + } + + logger.Verbosef("Interface %s created\n", interfaceName) + + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + logger.Errorf("Failed to create Docker client: %v", err) + os.Exit(ExitSetupFailed) + } + + logger.Verbosef("Wireguard server listening\n") + + ctx := context.Background() + + go func() { + for { + logger.Verbosef("Setting up Wireguard on Docker Desktop VM\n") + + err = setupVm(ctx, cli, port, hostPeerIp, vmPeerIp, hostPrivateKey, vmPrivateKey) + if err != nil { + logger.Errorf("Failed to setup VM: %v", err) + time.Sleep(5 * time.Second) + continue + } + + networks, err := cli.NetworkList(ctx, types.NetworkListOptions{}) + if err != nil { + logger.Errorf("Failed to list Docker networks: %v", err) + time.Sleep(5 * time.Second) + continue + } + + for _, network := range networks { + networkManager.ProcessDockerNetworkCreate(network, interfaceName) + } + + logger.Verbosef("Watching Docker events\n") + + msgs, errsChan := cli.Events(ctx, types.EventsOptions{ + Filters: filters.NewArgs( + filters.Arg("type", "network"), + filters.Arg("event", "create"), + filters.Arg("event", "destroy"), + ), + }) + + for loop := true; loop; { + select { + case err := <-errsChan: + logger.Errorf("Error: %v\n", err) + loop = false + case msg := <-msgs: + // Add routes when new Docker networks are created + if msg.Type == "network" && msg.Action == "create" { + network, err := cli.NetworkInspect(ctx, msg.Actor.ID, types.NetworkInspectOptions{}) + if err != nil { + logger.Errorf("Failed to inspect new Docker network: %v", err) + continue + } + + networkManager.ProcessDockerNetworkCreate(network, interfaceName) + continue + } + + // Delete routes when Docker networks are destroyed + if msg.Type == "network" && msg.Action == "destroy" { + network, exists := networkManager.DockerNetworks[msg.Actor.ID] + if !exists { + logger.Errorf("Unknown Docker network with ID %s. No routes will be removed.") + continue + } + + networkManager.ProcessDockerNetworkDestroy(network) + continue + } + } + } + + time.Sleep(5 * time.Second) + } + }() + + // Wait for program to terminate + + signal.Notify(term, syscall.SIGTERM) + signal.Notify(term, os.Interrupt) + + select { + case <-term: + case <-errs: + case <-device.Wait(): + } + + // Clean up + + uapi.Close() + device.Close() + + logger.Verbosef("Shutting down\n") +} + +func setupVm( + ctx context.Context, + dockerCli *client.Client, + serverPort int, + hostPeerIp string, + vmPeerIp string, + hostPrivateKey wgtypes.Key, + vmPrivateKey wgtypes.Key, +) error { + imageName := fmt.Sprintf("%s:%s", version.SetupImage, version.Version) + + _, _, err := dockerCli.ImageInspectWithRaw(ctx, imageName) + if err != nil { + fmt.Printf("Image doesn't exist locally. Pulling...\n") + + pullStream, err := dockerCli.ImagePull(ctx, imageName, image.PullOptions{}) + if err != nil { + return fmt.Errorf("failed to pull setup image: %w", err) + } + + io.Copy(os.Stdout, pullStream) + } + + resp, err := dockerCli.ContainerCreate(ctx, &container.Config{ + Image: imageName, + Env: []string{ + "SERVER_PORT=" + strconv.Itoa(serverPort), + "HOST_PEER_IP=" + hostPeerIp, + "VM_PEER_IP=" + vmPeerIp, + "HOST_PUBLIC_KEY=" + hostPrivateKey.PublicKey().String(), + "VM_PRIVATE_KEY=" + vmPrivateKey.String(), + }, + }, &container.HostConfig{ + AutoRemove: true, + NetworkMode: "host", + CapAdd: []string{"NET_ADMIN"}, + }, nil, nil, "wireguard-setup") + if err != nil { + return fmt.Errorf("failed to create container: %w", err) + } + + // Run container to completion + err = dockerCli.ContainerStart(ctx, resp.ID, container.StartOptions{}) + if err != nil { + return fmt.Errorf("failed to start container: %w", err) + } + + func() error { + reader, err := dockerCli.ContainerLogs(ctx, resp.ID, container.LogsOptions{ + ShowStdout: true, + ShowStderr: true, + Follow: true, + }) + if err != nil { + return fmt.Errorf("failed to get logs for container %s: %w", resp.ID, err) + } + + defer reader.Close() + + _, err = stdcopy.StdCopy(os.Stdout, os.Stderr, reader) + if err != nil { + return err + } + + return nil + }() + + fmt.Println("Setup container complete") + + return nil +} diff --git a/networkmanager/networkmanager.go b/networkmanager/networkmanager_darwin.go similarity index 97% rename from networkmanager/networkmanager.go rename to networkmanager/networkmanager_darwin.go index aae4d6d..6b5e23e 100644 --- a/networkmanager/networkmanager.go +++ b/networkmanager/networkmanager_darwin.go @@ -1,3 +1,5 @@ +//go:build darwin || dragonfly || freebsd || netbsd || openbsd + package networkmanager import ( diff --git a/networkmanager/networkmanager_windows.go b/networkmanager/networkmanager_windows.go new file mode 100644 index 0000000..97cc1b7 --- /dev/null +++ b/networkmanager/networkmanager_windows.go @@ -0,0 +1,100 @@ +//go:build windows + +package networkmanager + +import ( + "bytes" + "fmt" + "os/exec" + + "github.com/docker/docker/api/types" +) + +type NetworkManager struct { + DockerNetworks map[string]types.NetworkResource +} + +func New() NetworkManager { + return NetworkManager{ + DockerNetworks: map[string]types.NetworkResource{}, + } +} + +// Set the point-to-point IP address configuration on a network interface. +func (manager *NetworkManager) SetInterfaceAddress(ip string, peerIp string, iface string) (string, string, error) { + + cmd := exec.Command("netsh", "interface", "ipv4", "set", "address", "name=\""+iface+"\"", "static", ip, "255.255.255.255", peerIp, "gwmetric=100") + + var stdout bytes.Buffer + var stderr bytes.Buffer + + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + err := cmd.Run() + + return stdout.String(), stderr.String(), err +} + +// Add a route to the routing table. +func (manager *NetworkManager) AddRoute(net string, iface string) (string, string, error) { + + cmd := exec.Command("netsh", "interface", "ipv4", "add", "route", net, iface) + + var stdout bytes.Buffer + var stderr bytes.Buffer + + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + err := cmd.Run() + + return stdout.String(), stderr.String(), err +} + +// Delete a route from the routing table. +func (manager *NetworkManager) DeleteRoute(net string) (string, string, error) { + + cmd := exec.Command("netsh", "interface", "ipv4", "delete", "route", net) + + var stdout bytes.Buffer + var stderr bytes.Buffer + + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + err := cmd.Run() + + return stdout.String(), stderr.String(), err +} + +func (manager *NetworkManager) ProcessDockerNetworkCreate(network types.NetworkResource, iface string) { + manager.DockerNetworks[network.ID] = network + + for _, config := range network.IPAM.Config { + if network.Scope == "local" { + fmt.Printf("Adding route for %s -> %s (%s)\n", config.Subnet, iface, network.Name) + + _, stderr, err := manager.AddRoute(config.Subnet, iface) + + if err != nil { + fmt.Printf("Failed to add route: %v. %v\n", err, stderr) + } + } + } +} + +func (manager *NetworkManager) ProcessDockerNetworkDestroy(network types.NetworkResource) { + for _, config := range network.IPAM.Config { + if network.Scope == "local" { + fmt.Printf("Deleting route for %s (%s)\n", config.Subnet, network.Name) + + _, stderr, err := manager.DeleteRoute(config.Subnet) + + if err != nil { + fmt.Printf("Failed to delete route: %v. %v\n", err, stderr) + } + } + } + delete(manager.DockerNetworks, network.ID) +}