1package hcn
2
3import (
4	"encoding/json"
5
6	"github.com/Microsoft/hcsshim/internal/guid"
7	"github.com/Microsoft/hcsshim/internal/interop"
8	"github.com/sirupsen/logrus"
9)
10
11// Route is assoicated with a subnet.
12type Route struct {
13	NextHop           string `json:",omitempty"`
14	DestinationPrefix string `json:",omitempty"`
15	Metric            uint16 `json:",omitempty"`
16}
17
18// Subnet is assoicated with a Ipam.
19type Subnet struct {
20	IpAddressPrefix string            `json:",omitempty"`
21	Policies        []json.RawMessage `json:",omitempty"`
22	Routes          []Route           `json:",omitempty"`
23}
24
25// Ipam (Internet Protocol Addres Management) is assoicated with a network
26// and represents the address space(s) of a network.
27type Ipam struct {
28	Type    string   `json:",omitempty"` // Ex: Static, DHCP
29	Subnets []Subnet `json:",omitempty"`
30}
31
32// MacRange is associated with MacPool and respresents the start and end addresses.
33type MacRange struct {
34	StartMacAddress string `json:",omitempty"`
35	EndMacAddress   string `json:",omitempty"`
36}
37
38// MacPool is assoicated with a network and represents pool of MacRanges.
39type MacPool struct {
40	Ranges []MacRange `json:",omitempty"`
41}
42
43// Dns (Domain Name System is associated with a network.
44type Dns struct {
45	Domain     string   `json:",omitempty"`
46	Search     []string `json:",omitempty"`
47	ServerList []string `json:",omitempty"`
48	Options    []string `json:",omitempty"`
49}
50
51// NetworkType are various networks.
52type NetworkType string
53
54// NetworkType const
55const (
56	NAT         NetworkType = "NAT"
57	Transparent NetworkType = "Transparent"
58	L2Bridge    NetworkType = "L2Bridge"
59	L2Tunnel    NetworkType = "L2Tunnel"
60	ICS         NetworkType = "ICS"
61	Private     NetworkType = "Private"
62	Overlay     NetworkType = "Overlay"
63)
64
65// NetworkFlags are various network flags.
66type NetworkFlags uint32
67
68// NetworkFlags const
69const (
70	None                NetworkFlags = 0
71	EnableNonPersistent NetworkFlags = 8
72)
73
74// HostComputeNetwork represents a network
75type HostComputeNetwork struct {
76	Id            string          `json:"ID,omitempty"`
77	Name          string          `json:",omitempty"`
78	Type          NetworkType     `json:",omitempty"`
79	Policies      []NetworkPolicy `json:",omitempty"`
80	MacPool       MacPool         `json:",omitempty"`
81	Dns           Dns             `json:",omitempty"`
82	Ipams         []Ipam          `json:",omitempty"`
83	Flags         NetworkFlags    `json:",omitempty"` // 0: None
84	SchemaVersion SchemaVersion   `json:",omitempty"`
85}
86
87// NetworkResourceType are the 3 different Network settings resources.
88type NetworkResourceType string
89
90var (
91	// NetworkResourceTypePolicy is for Network's policies. Ex: RemoteSubnet
92	NetworkResourceTypePolicy NetworkResourceType = "Policy"
93	// NetworkResourceTypeDNS is for Network's DNS settings.
94	NetworkResourceTypeDNS NetworkResourceType = "DNS"
95	// NetworkResourceTypeExtension is for Network's extension settings.
96	NetworkResourceTypeExtension NetworkResourceType = "Extension"
97)
98
99// ModifyNetworkSettingRequest is the structure used to send request to modify an network.
100// Used to update DNS/extension/policy on an network.
101type ModifyNetworkSettingRequest struct {
102	ResourceType NetworkResourceType `json:",omitempty"` // Policy, DNS, Extension
103	RequestType  RequestType         `json:",omitempty"` // Add, Remove, Update, Refresh
104	Settings     json.RawMessage     `json:",omitempty"`
105}
106
107type PolicyNetworkRequest struct {
108	Policies []NetworkPolicy `json:",omitempty"`
109}
110
111func getNetwork(networkGuid guid.GUID, query string) (*HostComputeNetwork, error) {
112	// Open network.
113	var (
114		networkHandle    hcnNetwork
115		resultBuffer     *uint16
116		propertiesBuffer *uint16
117	)
118	hr := hcnOpenNetwork(&networkGuid, &networkHandle, &resultBuffer)
119	if err := checkForErrors("hcnOpenNetwork", hr, resultBuffer); err != nil {
120		return nil, err
121	}
122	// Query network.
123	hr = hcnQueryNetworkProperties(networkHandle, query, &propertiesBuffer, &resultBuffer)
124	if err := checkForErrors("hcnQueryNetworkProperties", hr, resultBuffer); err != nil {
125		return nil, err
126	}
127	properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer)
128	// Close network.
129	hr = hcnCloseNetwork(networkHandle)
130	if err := checkForErrors("hcnCloseNetwork", hr, nil); err != nil {
131		return nil, err
132	}
133	// Convert output to HostComputeNetwork
134	var outputNetwork HostComputeNetwork
135	if err := json.Unmarshal([]byte(properties), &outputNetwork); err != nil {
136		return nil, err
137	}
138	return &outputNetwork, nil
139}
140
141func enumerateNetworks(query string) ([]HostComputeNetwork, error) {
142	// Enumerate all Network Guids
143	var (
144		resultBuffer  *uint16
145		networkBuffer *uint16
146	)
147	hr := hcnEnumerateNetworks(query, &networkBuffer, &resultBuffer)
148	if err := checkForErrors("hcnEnumerateNetworks", hr, resultBuffer); err != nil {
149		return nil, err
150	}
151
152	networks := interop.ConvertAndFreeCoTaskMemString(networkBuffer)
153	var networkIds []guid.GUID
154	if err := json.Unmarshal([]byte(networks), &networkIds); err != nil {
155		return nil, err
156	}
157
158	var outputNetworks []HostComputeNetwork
159	for _, networkGuid := range networkIds {
160		network, err := getNetwork(networkGuid, query)
161		if err != nil {
162			return nil, err
163		}
164		outputNetworks = append(outputNetworks, *network)
165	}
166	return outputNetworks, nil
167}
168
169func createNetwork(settings string) (*HostComputeNetwork, error) {
170	// Create new network.
171	var (
172		networkHandle    hcnNetwork
173		resultBuffer     *uint16
174		propertiesBuffer *uint16
175	)
176	networkGuid := guid.GUID{}
177	hr := hcnCreateNetwork(&networkGuid, settings, &networkHandle, &resultBuffer)
178	if err := checkForErrors("hcnCreateNetwork", hr, resultBuffer); err != nil {
179		return nil, err
180	}
181	// Query network.
182	hcnQuery := defaultQuery()
183	query, err := json.Marshal(hcnQuery)
184	if err != nil {
185		return nil, err
186	}
187	hr = hcnQueryNetworkProperties(networkHandle, string(query), &propertiesBuffer, &resultBuffer)
188	if err := checkForErrors("hcnQueryNetworkProperties", hr, resultBuffer); err != nil {
189		return nil, err
190	}
191	properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer)
192	// Close network.
193	hr = hcnCloseNetwork(networkHandle)
194	if err := checkForErrors("hcnCloseNetwork", hr, nil); err != nil {
195		return nil, err
196	}
197	// Convert output to HostComputeNetwork
198	var outputNetwork HostComputeNetwork
199	if err := json.Unmarshal([]byte(properties), &outputNetwork); err != nil {
200		return nil, err
201	}
202	return &outputNetwork, nil
203}
204
205func modifyNetwork(networkId string, settings string) (*HostComputeNetwork, error) {
206	networkGuid := guid.FromString(networkId)
207	// Open Network
208	var (
209		networkHandle    hcnNetwork
210		resultBuffer     *uint16
211		propertiesBuffer *uint16
212	)
213	hr := hcnOpenNetwork(&networkGuid, &networkHandle, &resultBuffer)
214	if err := checkForErrors("hcnOpenNetwork", hr, resultBuffer); err != nil {
215		return nil, err
216	}
217	// Modify Network
218	hr = hcnModifyNetwork(networkHandle, settings, &resultBuffer)
219	if err := checkForErrors("hcnModifyNetwork", hr, resultBuffer); err != nil {
220		return nil, err
221	}
222	// Query network.
223	hcnQuery := defaultQuery()
224	query, err := json.Marshal(hcnQuery)
225	if err != nil {
226		return nil, err
227	}
228	hr = hcnQueryNetworkProperties(networkHandle, string(query), &propertiesBuffer, &resultBuffer)
229	if err := checkForErrors("hcnQueryNetworkProperties", hr, resultBuffer); err != nil {
230		return nil, err
231	}
232	properties := interop.ConvertAndFreeCoTaskMemString(propertiesBuffer)
233	// Close network.
234	hr = hcnCloseNetwork(networkHandle)
235	if err := checkForErrors("hcnCloseNetwork", hr, nil); err != nil {
236		return nil, err
237	}
238	// Convert output to HostComputeNetwork
239	var outputNetwork HostComputeNetwork
240	if err := json.Unmarshal([]byte(properties), &outputNetwork); err != nil {
241		return nil, err
242	}
243	return &outputNetwork, nil
244}
245
246func deleteNetwork(networkId string) error {
247	networkGuid := guid.FromString(networkId)
248	var resultBuffer *uint16
249	hr := hcnDeleteNetwork(&networkGuid, &resultBuffer)
250	if err := checkForErrors("hcnDeleteNetwork", hr, resultBuffer); err != nil {
251		return err
252	}
253	return nil
254}
255
256// ListNetworks makes a call to list all available networks.
257func ListNetworks() ([]HostComputeNetwork, error) {
258	hcnQuery := defaultQuery()
259	networks, err := ListNetworksQuery(hcnQuery)
260	if err != nil {
261		return nil, err
262	}
263	return networks, nil
264}
265
266// ListNetworksQuery makes a call to query the list of available networks.
267func ListNetworksQuery(query HostComputeQuery) ([]HostComputeNetwork, error) {
268	queryJson, err := json.Marshal(query)
269	if err != nil {
270		return nil, err
271	}
272
273	networks, err := enumerateNetworks(string(queryJson))
274	if err != nil {
275		return nil, err
276	}
277	return networks, nil
278}
279
280// GetNetworkByID returns the network specified by Id.
281func GetNetworkByID(networkID string) (*HostComputeNetwork, error) {
282	hcnQuery := defaultQuery()
283	mapA := map[string]string{"ID": networkID}
284	filter, err := json.Marshal(mapA)
285	if err != nil {
286		return nil, err
287	}
288	hcnQuery.Filter = string(filter)
289
290	networks, err := ListNetworksQuery(hcnQuery)
291	if err != nil {
292		return nil, err
293	}
294	if len(networks) == 0 {
295		return nil, NetworkNotFoundError{NetworkID: networkID}
296	}
297	return &networks[0], err
298}
299
300// GetNetworkByName returns the network specified by Name.
301func GetNetworkByName(networkName string) (*HostComputeNetwork, error) {
302	hcnQuery := defaultQuery()
303	mapA := map[string]string{"Name": networkName}
304	filter, err := json.Marshal(mapA)
305	if err != nil {
306		return nil, err
307	}
308	hcnQuery.Filter = string(filter)
309
310	networks, err := ListNetworksQuery(hcnQuery)
311	if err != nil {
312		return nil, err
313	}
314	if len(networks) == 0 {
315		return nil, NetworkNotFoundError{NetworkName: networkName}
316	}
317	return &networks[0], err
318}
319
320// Create Network.
321func (network *HostComputeNetwork) Create() (*HostComputeNetwork, error) {
322	logrus.Debugf("hcn::HostComputeNetwork::Create id=%s", network.Id)
323
324	jsonString, err := json.Marshal(network)
325	if err != nil {
326		return nil, err
327	}
328
329	logrus.Debugf("hcn::HostComputeNetwork::Create JSON: %s", jsonString)
330	network, hcnErr := createNetwork(string(jsonString))
331	if hcnErr != nil {
332		return nil, hcnErr
333	}
334	return network, nil
335}
336
337// Delete Network.
338func (network *HostComputeNetwork) Delete() error {
339	logrus.Debugf("hcn::HostComputeNetwork::Delete id=%s", network.Id)
340
341	if err := deleteNetwork(network.Id); err != nil {
342		return err
343	}
344	return nil
345}
346
347// ModifyNetworkSettings updates the Policy for a network.
348func (network *HostComputeNetwork) ModifyNetworkSettings(request *ModifyNetworkSettingRequest) error {
349	logrus.Debugf("hcn::HostComputeNetwork::ModifyNetworkSettings id=%s", network.Id)
350
351	networkSettingsRequest, err := json.Marshal(request)
352	if err != nil {
353		return err
354	}
355
356	_, err = modifyNetwork(network.Id, string(networkSettingsRequest))
357	if err != nil {
358		return err
359	}
360	return nil
361}
362
363// AddPolicy applies a Policy (ex: RemoteSubnet) on the Network.
364func (network *HostComputeNetwork) AddPolicy(networkPolicy PolicyNetworkRequest) error {
365	logrus.Debugf("hcn::HostComputeNetwork::AddPolicy id=%s", network.Id)
366
367	settingsJson, err := json.Marshal(networkPolicy)
368	if err != nil {
369		return err
370	}
371	requestMessage := &ModifyNetworkSettingRequest{
372		ResourceType: NetworkResourceTypePolicy,
373		RequestType:  RequestTypeAdd,
374		Settings:     settingsJson,
375	}
376
377	return network.ModifyNetworkSettings(requestMessage)
378}
379
380// RemovePolicy removes a Policy (ex: RemoteSubnet) from the Network.
381func (network *HostComputeNetwork) RemovePolicy(networkPolicy PolicyNetworkRequest) error {
382	logrus.Debugf("hcn::HostComputeNetwork::RemovePolicy id=%s", network.Id)
383
384	settingsJson, err := json.Marshal(networkPolicy)
385	if err != nil {
386		return err
387	}
388	requestMessage := &ModifyNetworkSettingRequest{
389		ResourceType: NetworkResourceTypePolicy,
390		RequestType:  RequestTypeRemove,
391		Settings:     settingsJson,
392	}
393
394	return network.ModifyNetworkSettings(requestMessage)
395}
396
397// CreateEndpoint creates an endpoint on the Network.
398func (network *HostComputeNetwork) CreateEndpoint(endpoint *HostComputeEndpoint) (*HostComputeEndpoint, error) {
399	isRemote := endpoint.Flags&EndpointFlagsRemoteEndpoint != 0
400	logrus.Debugf("hcn::HostComputeNetwork::CreatEndpoint, networkId=%s remote=%t", network.Id, isRemote)
401
402	endpoint.HostComputeNetwork = network.Id
403	endpointSettings, err := json.Marshal(endpoint)
404	if err != nil {
405		return nil, err
406	}
407	newEndpoint, err := createEndpoint(network.Id, string(endpointSettings))
408	if err != nil {
409		return nil, err
410	}
411	return newEndpoint, nil
412}
413
414// CreateRemoteEndpoint creates a remote endpoint on the Network.
415func (network *HostComputeNetwork) CreateRemoteEndpoint(endpoint *HostComputeEndpoint) (*HostComputeEndpoint, error) {
416	endpoint.Flags = EndpointFlagsRemoteEndpoint | endpoint.Flags
417	return network.CreateEndpoint(endpoint)
418}
419