1
2
3 package main
4
5 import (
6 "context"
7 "encoding/json"
8 "fmt"
9 "net"
10 "strings"
11
12 "github.com/Microsoft/hcsshim/hcn"
13 "github.com/Microsoft/hcsshim/internal/log"
14 ncproxygrpc "github.com/Microsoft/hcsshim/pkg/ncproxy/ncproxygrpc/v1"
15 "github.com/pkg/errors"
16 )
17
18 func hcnEndpointToEndpointResponse(ep *hcn.HostComputeEndpoint) (_ *ncproxygrpc.GetEndpointResponse, err error) {
19 hcnEndpointResp := &ncproxygrpc.HcnEndpointSettings{
20 Name: ep.Name,
21 Macaddress: ep.MacAddress,
22 NetworkName: ep.HostComputeNetwork,
23 DnsSetting: &ncproxygrpc.DnsSetting{
24 ServerIpAddrs: ep.Dns.ServerList,
25 Domain: ep.Dns.Domain,
26 Search: ep.Dns.Search,
27 },
28 }
29
30 policies, err := parseEndpointPolicies(ep.Policies)
31 if err != nil {
32 return nil, err
33 }
34 hcnEndpointResp.Policies = policies
35
36 ipConfigInfos := ep.IpConfigurations
37
38 if len(ipConfigInfos) == 0 || len(ipConfigInfos) > 2 {
39 return nil, errors.Errorf("invalid number (%v) of ip configuration information for endpoint %v", len(ipConfigInfos), ep.Name)
40 }
41 for _, ipConfig := range ipConfigInfos {
42 ip := net.ParseIP(ipConfig.IpAddress)
43 if ip == nil {
44 return nil, errors.Errorf("failed to parse IP address %v", ipConfig.IpAddress)
45 }
46 if ip.To4() != nil {
47
48 hcnEndpointResp.Ipaddress = ipConfig.IpAddress
49 hcnEndpointResp.IpaddressPrefixlength = uint32(ipConfig.PrefixLength)
50 } else {
51
52 hcnEndpointResp.Ipv6Address = ipConfig.IpAddress
53 hcnEndpointResp.Ipv6AddressPrefixlength = uint32(ipConfig.PrefixLength)
54 }
55 }
56
57 return &ncproxygrpc.GetEndpointResponse{
58 Namespace: ep.HostComputeNamespace,
59 ID: ep.Id,
60 Endpoint: &ncproxygrpc.EndpointSettings{
61 Settings: &ncproxygrpc.EndpointSettings_HcnEndpoint{
62 HcnEndpoint: hcnEndpointResp,
63 },
64 },
65 }, nil
66 }
67
68 func modifyEndpoint(ctx context.Context, id string, policies []hcn.EndpointPolicy, requestType hcn.RequestType) error {
69 endpointRequest := hcn.PolicyEndpointRequest{
70 Policies: policies,
71 }
72
73 settingsJSON, err := json.Marshal(endpointRequest)
74 if err != nil {
75 return err
76 }
77
78 requestMessage := &hcn.ModifyEndpointSettingRequest{
79 ResourceType: hcn.EndpointResourceTypePolicy,
80 RequestType: requestType,
81 Settings: settingsJSON,
82 }
83
84 return hcn.ModifyEndpointSettings(id, requestMessage)
85 }
86
87 func parseEndpointPolicies(policies []hcn.EndpointPolicy) (*ncproxygrpc.HcnEndpointPolicies, error) {
88 results := &ncproxygrpc.HcnEndpointPolicies{}
89 for _, policy := range policies {
90 switch policy.Type {
91 case hcn.PortMapping:
92 portMapSettings := &hcn.PortnameEndpointPolicySetting{}
93 if err := json.Unmarshal(policy.Settings, portMapSettings); err != nil {
94 return nil, err
95 }
96 results.PortnamePolicySetting = &ncproxygrpc.PortNameEndpointPolicySetting{
97 PortName: portMapSettings.Name,
98 }
99 case hcn.IOV:
100 iovSettings := &hcn.IovPolicySetting{}
101 if err := json.Unmarshal(policy.Settings, iovSettings); err != nil {
102 return nil, err
103 }
104 results.IovPolicySettings = &ncproxygrpc.IovEndpointPolicySetting{
105 IovOffloadWeight: iovSettings.IovOffloadWeight,
106 QueuePairsRequested: iovSettings.QueuePairsRequested,
107 InterruptModeration: iovSettings.InterruptModeration,
108 }
109 }
110 }
111 return results, nil
112 }
113
114 func constructEndpointPolicies(req *ncproxygrpc.HcnEndpointPolicies) ([]hcn.EndpointPolicy, error) {
115 policies := []hcn.EndpointPolicy{}
116 if req.IovPolicySettings != nil {
117 iovSettings := hcn.IovPolicySetting{
118 IovOffloadWeight: req.IovPolicySettings.IovOffloadWeight,
119 QueuePairsRequested: req.IovPolicySettings.QueuePairsRequested,
120 InterruptModeration: req.IovPolicySettings.InterruptModeration,
121 }
122 iovJSON, err := json.Marshal(iovSettings)
123 if err != nil {
124 return []hcn.EndpointPolicy{}, errors.Wrap(err, "failed to marshal IovPolicySettings")
125 }
126 policy := hcn.EndpointPolicy{
127 Type: hcn.IOV,
128 Settings: iovJSON,
129 }
130 policies = append(policies, policy)
131 }
132
133 if req.PortnamePolicySetting != nil {
134 portPolicy := hcn.PortnameEndpointPolicySetting{
135 Name: req.PortnamePolicySetting.PortName,
136 }
137 portPolicyJSON, err := json.Marshal(portPolicy)
138 if err != nil {
139 return []hcn.EndpointPolicy{}, errors.Wrap(err, "failed to marshal portname")
140 }
141 policy := hcn.EndpointPolicy{
142 Type: hcn.PortName,
143 Settings: portPolicyJSON,
144 }
145 policies = append(policies, policy)
146 }
147
148 return policies, nil
149 }
150
151 func createHCNNetwork(ctx context.Context, req *ncproxygrpc.HostComputeNetworkSettings) (*hcn.HostComputeNetwork, error) {
152
153 _, err := hcn.GetNetworkByName(req.Name)
154 if err == nil {
155 return nil, errors.Errorf("network with name %q already exists", req.Name)
156 }
157
158 policies := []hcn.NetworkPolicy{}
159 if req.SwitchName != "" {
160
161
162
163 extSwitch, err := hcn.GetNetworkByName(req.SwitchName)
164 if err != nil {
165 if _, ok := err.(hcn.NetworkNotFoundError); ok {
166 return nil, errors.Errorf("no network/switch with name `%s` found", req.SwitchName)
167 }
168 return nil, errors.Wrapf(err, "failed to get network/switch with name %q", req.SwitchName)
169 }
170
171
172 if extSwitch.Health.Extra.LayeredOn == "" {
173 return nil, errors.Errorf("no layer ID found for network %q found", extSwitch.Id)
174 }
175
176 layerPolicy := hcn.LayerConstraintNetworkPolicySetting{LayerId: extSwitch.Health.Extra.LayeredOn}
177 data, err := json.Marshal(layerPolicy)
178 if err != nil {
179 return nil, errors.Wrap(err, "failed to marshal layer policy")
180 }
181
182 netPolicy := hcn.NetworkPolicy{
183 Type: hcn.LayerConstraint,
184 Settings: data,
185 }
186 policies = append(policies, netPolicy)
187 }
188
189 subnets := make([]hcn.Subnet, 0, len(req.SubnetIpaddressPrefix)+len(req.SubnetIpaddressPrefixIpv6))
190 for _, addrPrefix := range req.SubnetIpaddressPrefix {
191 subnet := hcn.Subnet{
192 IpAddressPrefix: addrPrefix,
193 Routes: []hcn.Route{
194 {
195 NextHop: req.DefaultGateway,
196 DestinationPrefix: "0.0.0.0/0",
197 },
198 },
199 }
200 subnets = append(subnets, subnet)
201 }
202
203 if len(req.SubnetIpaddressPrefixIpv6) != 0 {
204 if err := hcn.IPv6DualStackSupported(); err != nil {
205
206 return nil, fmt.Errorf("IPv6 address requested but not supported: %v", err)
207 }
208 }
209
210 for _, ipv6AddrPrefix := range req.SubnetIpaddressPrefixIpv6 {
211 subnet := hcn.Subnet{
212 IpAddressPrefix: ipv6AddrPrefix,
213 Routes: []hcn.Route{
214 {
215 NextHop: req.DefaultGatewayIpv6,
216 DestinationPrefix: "::/0",
217 },
218 },
219 }
220 subnets = append(subnets, subnet)
221 }
222
223 ipam := hcn.Ipam{
224 Type: req.IpamType.String(),
225 Subnets: subnets,
226 }
227
228 network := &hcn.HostComputeNetwork{
229 Name: req.Name,
230 Type: hcn.NetworkType(req.Mode.String()),
231 Ipams: []hcn.Ipam{ipam},
232 Policies: policies,
233 SchemaVersion: hcn.SchemaVersion{
234 Major: 2,
235 Minor: 2,
236 },
237 }
238
239 network, err = network.Create()
240 if err != nil {
241 return nil, errors.Wrapf(err, "failed to create HNS network %q", req.Name)
242 }
243
244 return network, nil
245 }
246
247 func hcnNetworkToNetworkResponse(ctx context.Context, network *hcn.HostComputeNetwork) (*ncproxygrpc.GetNetworkResponse, error) {
248 var (
249 ipamType int32
250 defaultGateway string
251 defaultGatewayIPv6 string
252 switchName string
253 subnetIPAddressPrefixes []string
254 subnetIPv6AddressPrefixes []string
255 )
256
257 for _, ipam := range network.Ipams {
258
259 ipamType = ncproxygrpc.HostComputeNetworkSettings_IpamType_value[ipam.Type]
260 for _, subnet := range ipam.Subnets {
261
262 ipParts := strings.Split(subnet.IpAddressPrefix, "/")
263 ipPrefix := net.ParseIP(ipParts[0])
264 if ipPrefix == nil {
265 return nil, fmt.Errorf("failed to parse IP address %v", ipPrefix)
266 }
267 if ipPrefix.To4() != nil {
268
269 subnetIPAddressPrefixes = append(subnetIPAddressPrefixes, subnet.IpAddressPrefix)
270 if len(subnet.Routes) != 0 {
271 defaultGateway = subnet.Routes[0].NextHop
272 }
273 } else {
274
275 subnetIPv6AddressPrefixes = append(subnetIPv6AddressPrefixes, subnet.IpAddressPrefix)
276 if len(subnet.Routes) != 0 {
277 defaultGatewayIPv6 = subnet.Routes[0].NextHop
278 }
279 }
280 }
281 }
282
283 mode := ncproxygrpc.HostComputeNetworkSettings_NetworkMode_value[string(network.Type)]
284
285 if network.Health.Extra.SwitchGuid != "" {
286 extSwitch, err := hcn.GetNetworkByID(network.Health.Extra.SwitchGuid)
287 if err != nil {
288 return nil, err
289 }
290 switchName = extSwitch.Name
291 }
292
293 settings := &ncproxygrpc.HostComputeNetworkSettings{
294 Name: network.Name,
295 Mode: ncproxygrpc.HostComputeNetworkSettings_NetworkMode(mode),
296 SwitchName: switchName,
297 IpamType: ncproxygrpc.HostComputeNetworkSettings_IpamType(ipamType),
298 SubnetIpaddressPrefix: subnetIPAddressPrefixes,
299 DefaultGateway: defaultGateway,
300 SubnetIpaddressPrefixIpv6: subnetIPv6AddressPrefixes,
301 DefaultGatewayIpv6: defaultGatewayIPv6,
302 }
303
304 var startMac, endMac string
305 switch n := len(network.MacPool.Ranges); {
306 case n < 1:
307 return nil, fmt.Errorf("network %s(%s) MAC pool is empty", network.Name, network.Id)
308 case n > 1:
309 log.G(ctx).WithField("networkName", network.Name).Warn("network has multiple MAC pools, only returning the first")
310 fallthrough
311 default:
312 startMac = network.MacPool.Ranges[0].StartMacAddress
313 endMac = network.MacPool.Ranges[0].EndMacAddress
314 }
315
316 return &ncproxygrpc.GetNetworkResponse{
317 ID: network.Id,
318 Network: &ncproxygrpc.Network{
319 Settings: &ncproxygrpc.Network_HcnNetwork{
320 HcnNetwork: settings,
321 },
322 },
323 MacRange: &ncproxygrpc.MacRange{
324 StartMacAddress: startMac,
325 EndMacAddress: endMac,
326 },
327 }, nil
328 }
329
330 func createHCNEndpoint(ctx context.Context, network *hcn.HostComputeNetwork, req *ncproxygrpc.HcnEndpointSettings) (*hcn.HostComputeEndpoint, error) {
331
332 ipConfigs := []hcn.IpConfig{}
333 if req.Ipaddress != "" && req.IpaddressPrefixlength != 0 {
334 ipv4Config := hcn.IpConfig{
335 IpAddress: req.Ipaddress,
336 PrefixLength: uint8(req.IpaddressPrefixlength),
337 }
338 ipConfigs = append(ipConfigs, ipv4Config)
339 }
340
341 if req.Ipv6Address != "" && req.Ipv6AddressPrefixlength != 0 {
342 if err := hcn.IPv6DualStackSupported(); err != nil {
343
344 return nil, fmt.Errorf("IPv6 address requested but not supported: %v", err)
345 }
346 ipv6Config := hcn.IpConfig{
347 IpAddress: req.Ipv6Address,
348 PrefixLength: uint8(req.Ipv6AddressPrefixlength),
349 }
350 ipConfigs = append(ipConfigs, ipv6Config)
351 }
352
353 var err error
354 policies := []hcn.EndpointPolicy{}
355 if req.Policies != nil {
356 policies, err = constructEndpointPolicies(req.Policies)
357 if err != nil {
358 return nil, errors.Wrap(err, "failed to construct endpoint policies")
359 }
360 }
361
362 endpoint := &hcn.HostComputeEndpoint{
363 Name: req.Name,
364 HostComputeNetwork: network.Id,
365 MacAddress: req.Macaddress,
366 IpConfigurations: ipConfigs,
367 Policies: policies,
368 SchemaVersion: hcn.SchemaVersion{
369 Major: 2,
370 Minor: 0,
371 },
372 }
373
374 if req.DnsSetting != nil {
375 endpoint.Dns = hcn.Dns{
376 ServerList: req.DnsSetting.ServerIpAddrs,
377 Domain: req.DnsSetting.Domain,
378 Search: req.DnsSetting.Search,
379 }
380 }
381 endpoint, err = endpoint.Create()
382 if err != nil {
383 return nil, errors.Wrap(err, "failed to create HNS endpoint")
384 }
385
386 return endpoint, nil
387 }
388
389
390
391 func getHostDefaultNamespace() (string, error) {
392 namespaces, err := hcn.ListNamespaces()
393 if err != nil {
394 return "", errors.Wrapf(err, "failed list namespaces")
395 }
396
397 for _, ns := range namespaces {
398 if ns.Type == hcn.NamespaceTypeHostDefault {
399 return ns.Id, nil
400 }
401 }
402 return "", errors.New("unable to find default host namespace to attach to")
403 }
404
View as plain text