forked from yamt/midonet-kubernetes
/
sub.go
122 lines (115 loc) · 3.89 KB
/
sub.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
// Copyright (C) 2018 Midokura SARL.
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package endpoints
import (
"fmt"
"k8s.io/api/core/v1"
"github.com/midonet/midonet-kubernetes/pkg/converter"
"github.com/midonet/midonet-kubernetes/pkg/midonet"
)
type endpoint struct {
endpointsKey string
portName string
svcIP string
ip string
port int
protocol v1.Protocol
}
func (ep *endpoint) portKey() string {
// Note: portKey format should be consistent with the
// service converter.
return fmt.Sprintf("%s/%s", ep.endpointsKey, ep.portName)
}
func (ep *endpoint) Convert(epKey converter.Key, config *converter.Config) ([]converter.BackendResource, error) {
// REVISIT: An assumption here is that, if ServicePort.Name is empty,
// the corresponding EndpointPort.Name is also empty. It isn't clear
// to me (yamamoto) from the documentation.
portKey := ep.portKey()
portChainID := converter.IDForKey("ServicePort", portKey, config)
baseID := converter.IDForKey("Endpoint", epKey.Key(), config)
epChainID := baseID
epJumpRuleID := converter.SubID(baseID, "Jump to Endpoint")
epDNATRuleID := converter.SubID(baseID, "DNAT")
epSNATRuleID := converter.SubID(baseID, "SNAT")
return []converter.BackendResource{
&midonet.Chain{
ID: &epChainID,
Name: fmt.Sprintf("KUBE-SEP-%s", epKey.Key()),
TenantID: config.Tenant,
},
// REVISIT: kube-proxy implements load-balancing with its
// equivalent of this rule, using iptables probabilistic
// match. We can probably implement something similar
// here if the backend has the following functionalities.
//
// 1. probabilistic match
// 2. applyIfExists equivalent
//
// For now, we just install a normal 100% matching rule.
// It means that the endpoint which happens to have its
// jump rule the earliest in the chain handles 100% of
// traffic.
&midonet.Rule{
Parent: midonet.Parent{ID: &portChainID},
ID: &epJumpRuleID,
Type: "jump",
JumpChainID: &epChainID,
},
&midonet.Rule{
Parent: midonet.Parent{ID: &epChainID},
ID: &epDNATRuleID,
Type: "dnat",
NATTargets: &[]midonet.NATTarget{
{
AddressFrom: ep.ip,
AddressTo: ep.ip,
PortFrom: ep.port,
PortTo: ep.port,
},
},
FlowAction: "accept",
},
// SNAT traffic from the endpoint itself. Otherwise,
// the return traffic doesn't work.
// Note: Endpoint IP might or might not belong to the cluster ip
// range. It can be external.
//
// The source IP to use for this purpose is somewhat arbitrary
// and doesn't seem consistent among networking implementations.
// We use the ClusterIP of the corresponding Service.
// For example, kube-proxy uses iptables MASQUERADE target for
// this purpose. It means that the source IP of the outgoing
// interface is chosen after an L3 routing decision. With flannel,
// it would be the address of the cni0 interface on the node.
&midonet.Rule{
Parent: midonet.Parent{ID: &epChainID},
ID: &epSNATRuleID,
Type: "snat",
DLType: 0x800,
NWSrcAddress: ep.ip,
NWSrcLength: 32,
NATTargets: &[]midonet.NATTarget{
{
AddressFrom: ep.svcIP,
AddressTo: ep.svcIP,
// REVISIT: arbitrary port range
PortFrom: 30000,
PortTo: 60000,
},
},
FlowAction: "continue",
},
}, nil
}