1
2
3
4 package hcsv2
5
6 import (
7 "context"
8 "fmt"
9 "strings"
10 "sync"
11 "time"
12
13 "github.com/pkg/errors"
14 "github.com/vishvananda/netns"
15 "go.opencensus.io/trace"
16
17 "github.com/Microsoft/hcsshim/internal/guest/gcserr"
18 "github.com/Microsoft/hcsshim/internal/guest/network"
19 "github.com/Microsoft/hcsshim/internal/guest/prot"
20 "github.com/Microsoft/hcsshim/internal/oc"
21 "github.com/Microsoft/hcsshim/internal/protocol/guestresource"
22 )
23
24 var (
25
26 namespaceSync sync.Mutex
27
28
29
30 namespaces map[string]*namespace
31
32 networkInstanceIDToName = network.InstanceIDToName
33 )
34
35 func init() {
36 namespaces = make(map[string]*namespace)
37 }
38
39
40
41 func getNetworkNamespace(id string) (*namespace, error) {
42 id = strings.ToLower(id)
43
44 namespaceSync.Lock()
45 defer namespaceSync.Unlock()
46
47 ns, ok := namespaces[id]
48 if !ok {
49 return nil, gcserr.WrapHresult(errors.Errorf("namespace '%s' not found", id), gcserr.HrErrNotFound)
50 }
51 return ns, nil
52 }
53
54
55
56 func GetOrAddNetworkNamespace(id string) *namespace {
57 id = strings.ToLower(id)
58
59 namespaceSync.Lock()
60 defer namespaceSync.Unlock()
61
62 ns, ok := namespaces[id]
63 if !ok {
64 ns = &namespace{
65 id: id,
66 }
67 namespaces[id] = ns
68 }
69 return ns
70 }
71
72
73 func RemoveNetworkNamespace(ctx context.Context, id string) (err error) {
74 _, span := oc.StartSpan(ctx, "hcsv2::RemoveNetworkNamespace")
75 defer span.End()
76 defer func() { oc.SetSpanStatus(span, err) }()
77
78 id = strings.ToLower(id)
79 span.AddAttributes(trace.StringAttribute("id", id))
80
81 namespaceSync.Lock()
82 defer namespaceSync.Unlock()
83
84 ns, ok := namespaces[id]
85 if ok {
86 ns.m.Lock()
87 defer ns.m.Unlock()
88 if len(ns.nics) > 0 {
89 return errors.Errorf("network namespace '%s' contains adapters", id)
90 }
91 delete(namespaces, id)
92 }
93
94 return nil
95 }
96
97
98 type namespace struct {
99 id string
100
101 m sync.Mutex
102 pid int
103 nics []*nicInNamespace
104 }
105
106
107 func (n *namespace) ID() string {
108 return n.id
109 }
110
111
112
113
114 func (n *namespace) AssignContainerPid(ctx context.Context, pid int) (err error) {
115 _, span := oc.StartSpan(ctx, "namespace::AssignContainerPid")
116 defer span.End()
117 defer func() { oc.SetSpanStatus(span, err) }()
118 span.AddAttributes(
119 trace.StringAttribute("namespace", n.id),
120 trace.Int64Attribute("pid", int64(pid)))
121
122 n.m.Lock()
123 defer n.m.Unlock()
124
125 if n.pid != 0 {
126 return errors.Errorf("previously assigned container pid %d to network namespace %q", n.pid, n.id)
127 }
128
129 n.pid = pid
130 return nil
131 }
132
133
134
135 func (n *namespace) Adapters() []*guestresource.LCOWNetworkAdapter {
136 n.m.Lock()
137 defer n.m.Unlock()
138
139 adps := make([]*guestresource.LCOWNetworkAdapter, len(n.nics))
140 for i, nin := range n.nics {
141 adps[i] = nin.adapter
142 }
143 return adps
144 }
145
146
147
148
149 func (n *namespace) AddAdapter(ctx context.Context, adp *guestresource.LCOWNetworkAdapter) (err error) {
150 ctx, span := oc.StartSpan(ctx, "namespace::AddAdapter")
151 defer span.End()
152 defer func() { oc.SetSpanStatus(span, err) }()
153 span.AddAttributes(
154 trace.StringAttribute("namespace", n.id),
155 trace.StringAttribute("adapter", fmt.Sprintf("%+v", adp)))
156
157 n.m.Lock()
158 defer n.m.Unlock()
159
160 for _, nic := range n.nics {
161 if strings.EqualFold(nic.adapter.ID, adp.ID) {
162 return errors.Errorf("adapter with id: '%s' already present in namespace", adp.ID)
163 }
164 }
165
166 resolveCtx, cancel := context.WithTimeout(ctx, time.Second*5)
167 defer cancel()
168 ifname, err := networkInstanceIDToName(resolveCtx, adp.ID, adp.VPCIAssigned)
169 if err != nil {
170 return err
171 }
172 n.nics = append(n.nics, &nicInNamespace{
173 adapter: adp,
174 ifname: ifname,
175 })
176 return nil
177 }
178
179
180
181 func (n *namespace) RemoveAdapter(ctx context.Context, id string) (err error) {
182 _, span := oc.StartSpan(ctx, "namespace::RemoveAdapter")
183 defer span.End()
184 defer func() { oc.SetSpanStatus(span, err) }()
185 span.AddAttributes(
186 trace.StringAttribute("namespace", n.id),
187 trace.StringAttribute("adapterID", id))
188
189 n.m.Lock()
190 defer n.m.Unlock()
191
192
193
194 i := -1
195 for j, nic := range n.nics {
196 if strings.EqualFold(nic.adapter.ID, id) {
197 i = j
198 break
199 }
200 }
201 if i > -1 {
202 n.nics = append(n.nics[:i], n.nics[i+1:]...)
203 }
204 return nil
205 }
206
207
208 func (n *namespace) Sync(ctx context.Context) (err error) {
209 ctx, span := oc.StartSpan(ctx, "namespace::Sync")
210 defer span.End()
211 defer func() { oc.SetSpanStatus(span, err) }()
212 span.AddAttributes(trace.StringAttribute("namespace", n.id))
213
214 n.m.Lock()
215 defer n.m.Unlock()
216
217 if n.pid != 0 {
218 for i, a := range n.nics {
219
220
221 if i > 0 {
222 a.adapter.EnableLowMetric = true
223 }
224 err = a.assignToPid(ctx, n.pid)
225 if err != nil {
226 return err
227 }
228 }
229 }
230 return nil
231 }
232
233
234
235 type nicInNamespace struct {
236
237 adapter *guestresource.LCOWNetworkAdapter
238
239 ifname string
240
241
242 assignedPid int
243 }
244
245
246 func (nin *nicInNamespace) assignToPid(ctx context.Context, pid int) (err error) {
247 ctx, span := oc.StartSpan(ctx, "nicInNamespace::assignToPid")
248 defer span.End()
249 defer func() { oc.SetSpanStatus(span, err) }()
250 span.AddAttributes(
251 trace.StringAttribute("adapterID", nin.adapter.ID),
252 trace.StringAttribute("ifname", nin.ifname),
253 trace.Int64Attribute("pid", int64(pid)))
254
255 v1Adapter := &prot.NetworkAdapter{
256 NatEnabled: (nin.adapter.IPAddress != "") || (nin.adapter.IPv6Address != ""),
257 AllocatedIPAddress: nin.adapter.IPAddress,
258 HostIPAddress: nin.adapter.GatewayAddress,
259 HostIPPrefixLength: nin.adapter.PrefixLength,
260 AllocatedIPv6Address: nin.adapter.IPv6Address,
261 HostIPv6Address: nin.adapter.IPv6GatewayAddress,
262 HostIPv6PrefixLength: nin.adapter.IPv6PrefixLength,
263 EnableLowMetric: nin.adapter.EnableLowMetric,
264 EncapOverhead: nin.adapter.EncapOverhead,
265 }
266
267 if err := network.MoveInterfaceToNS(nin.ifname, pid); err != nil {
268 return errors.Wrapf(err, "failed to move interface %s to network namespace", nin.ifname)
269 }
270
271
272 ns, err := netns.GetFromPid(pid)
273 if err != nil {
274 return errors.Wrapf(err, "netns.GetFromPid(%d) failed", pid)
275 }
276 defer ns.Close()
277
278 netNSCfg := func() error {
279 return network.NetNSConfig(ctx, nin.ifname, pid, v1Adapter)
280 }
281
282 if err := network.DoInNetNS(ns, netNSCfg); err != nil {
283 return errors.Wrapf(err, "failed to configure adapter aid: %s, if id: %s", nin.adapter.ID, nin.ifname)
284 }
285 nin.assignedPid = pid
286 return nil
287 }
288
View as plain text