1// Copyright 2016 VMware, Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package network
16
17import (
18	"fmt"
19	"net"
20
21	"github.com/vmware/vic/lib/portlayer/exec"
22	"github.com/vmware/vic/pkg/ip"
23	"github.com/vmware/vic/pkg/uid"
24)
25
26type alias struct {
27	Name      string
28	Container string
29
30	ep *Endpoint
31}
32
33var badAlias = alias{}
34
35type Endpoint struct {
36	container *Container
37	scope     *Scope
38	ip        net.IP
39	static    bool
40	ports     map[Port]interface{} // exposed ports
41	aliases   map[string][]alias
42	gw        *net.IP
43	subnet    *net.IPNet
44}
45
46// scopeName returns the "fully qualified" name of an alias. Aliases are scoped
47// by the container and network scope they are in.
48func (a alias) scopedName() string {
49	// an alias for the container itself is network scoped
50	for _, al := range a.ep.getAliases("") {
51		if a.Name == al.Name {
52			return ScopedAliasName(a.ep.Scope().Name(), "", a.Name)
53		}
54	}
55
56	return ScopedAliasName(a.ep.Scope().Name(), a.ep.Container().Name(), a.Name)
57}
58
59// ScopedAliasName returns the fully qualified name of an alias, scoped to
60// the scope and optionally a container
61func ScopedAliasName(scope string, container string, alias string) string {
62	if container != "" {
63		return fmt.Sprintf("%s:%s:%s", scope, container, alias)
64	}
65
66	return fmt.Sprintf("%s:%s", scope, alias)
67}
68
69func newEndpoint(container *Container, scope *Scope, eip *net.IP, pciSlot *int32) *Endpoint {
70	e := &Endpoint{
71		container: container,
72		scope:     scope,
73		ip:        net.IPv4(0, 0, 0, 0),
74		static:    false,
75		ports:     make(map[Port]interface{}),
76		aliases:   make(map[string][]alias),
77	}
78
79	if eip != nil && !ip.IsUnspecifiedIP(*eip) {
80		e.ip = *eip
81		e.static = true
82	}
83
84	return e
85}
86
87func removeEndpointHelper(ep *Endpoint, eps []*Endpoint) []*Endpoint {
88	for i, e := range eps {
89		if ep != e {
90			continue
91		}
92
93		return append(eps[:i], eps[i+1:]...)
94	}
95
96	return eps
97}
98
99func (e *Endpoint) addPort(p Port) error {
100	if _, ok := e.ports[p]; ok {
101		return fmt.Errorf("port %s already exposed", p)
102	}
103
104	e.ports[p] = nil
105	return nil
106}
107
108func (e *Endpoint) IP() net.IP {
109	return e.ip
110}
111
112func (e *Endpoint) Scope() *Scope {
113	return e.scope
114}
115
116func (e *Endpoint) Subnet() *net.IPNet {
117	if e.subnet != nil {
118		return e.subnet
119	}
120
121	return e.Scope().Subnet()
122}
123
124func (e *Endpoint) Container() *Container {
125	return e.container
126}
127
128func (e *Endpoint) ID() uid.UID {
129	return e.container.ID()
130}
131
132func (e *Endpoint) Name() string {
133	return e.container.Name()
134}
135
136func (e *Endpoint) Gateway() net.IP {
137	if e.gw != nil {
138		return *e.gw
139	}
140
141	return e.Scope().Gateway()
142}
143
144func (e *Endpoint) Ports() []Port {
145	ports := make([]Port, len(e.ports))
146	i := 0
147	for p := range e.ports {
148		ports[i] = p
149		i++
150	}
151
152	return ports
153}
154
155func (e *Endpoint) addAlias(con, a string) (alias, bool) {
156	if a == "" {
157		return badAlias, false
158	}
159
160	if con == "" {
161		con = e.container.Name()
162	}
163
164	aliases := e.aliases[con]
165	for _, as := range aliases {
166		if as.Name == a {
167			// already present
168			return as, true
169		}
170	}
171
172	na := alias{
173		Name:      a,
174		Container: con,
175		ep:        e,
176	}
177	e.aliases[con] = append(aliases, na)
178	return na, false
179}
180
181func (e *Endpoint) getAliases(con string) []alias {
182	if con == "" {
183		con = e.container.Name()
184	}
185
186	return e.aliases[con]
187}
188
189func (e *Endpoint) copy() *Endpoint {
190	other := *e
191	other.aliases = make(map[string][]alias)
192	for k, v := range e.aliases {
193		a := make([]alias, len(v))
194		copy(a, v)
195		other.aliases[k] = a
196	}
197	other.ports = make(map[Port]interface{})
198	for p := range e.ports {
199		other.ports[p] = nil
200	}
201
202	return &other
203}
204
205func (e *Endpoint) refresh(h *exec.Handle) error {
206	if !e.scope.isDynamic() {
207		return nil
208	}
209
210	s := e.scope
211	ne := h.ExecConfig.Networks[s.Name()]
212	if ne == nil {
213		return fmt.Errorf("container config does not have info for network scope %s", s.Name())
214	}
215
216	if ip.IsUnspecifiedSubnet(&ne.Network.Assigned.Gateway) {
217		return fmt.Errorf("updating endpoint for container %s: gateway not present for scope %s", h.ExecConfig.ID, s.name)
218	}
219
220	gw, snet, err := net.ParseCIDR(ne.Network.Assigned.Gateway.String())
221	if err != nil {
222		return fmt.Errorf("could not parse gateway for container %s: %s", h.ExecConfig.ID, err)
223	}
224
225	e.ip = ne.Assigned.IP
226	e.gw = &gw
227	e.subnet = snet
228	return nil
229}
230