blob: 93bde9b7b441b47f90467bbe685dccd391ea64df [file] [log] [blame]
Jian Li1b08d652019-05-02 17:28:09 +09001/*
2 * Copyright 2019-present Open Networking Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.k8snetworking.impl;
17
18import org.onlab.packet.ARP;
19import org.onlab.packet.Ethernet;
20import org.onlab.packet.Ip4Address;
21import org.onlab.packet.IpAddress;
22import org.onlab.packet.MacAddress;
23import org.onlab.packet.VlanId;
24import org.onosproject.cluster.ClusterService;
25import org.onosproject.cluster.LeadershipService;
26import org.onosproject.cluster.NodeId;
27import org.onosproject.core.ApplicationId;
28import org.onosproject.core.CoreService;
29import org.onosproject.k8snetworking.api.K8sFlowRuleService;
Jian Lieab51352020-09-11 03:29:16 +090030import org.onosproject.k8snode.api.K8sHostAdminService;
Jian Li1b08d652019-05-02 17:28:09 +090031import org.onosproject.k8snode.api.K8sNode;
32import org.onosproject.k8snode.api.K8sNodeAdminService;
33import org.onosproject.k8snode.api.K8sNodeEvent;
34import org.onosproject.k8snode.api.K8sNodeListener;
Jian Lieab51352020-09-11 03:29:16 +090035import org.onosproject.net.DeviceId;
Jian Li1b08d652019-05-02 17:28:09 +090036import org.onosproject.net.flow.DefaultTrafficSelector;
37import org.onosproject.net.flow.DefaultTrafficTreatment;
38import org.onosproject.net.flow.TrafficSelector;
39import org.onosproject.net.flow.TrafficTreatment;
40import org.onosproject.net.packet.DefaultOutboundPacket;
41import org.onosproject.net.packet.InboundPacket;
42import org.onosproject.net.packet.PacketContext;
43import org.onosproject.net.packet.PacketProcessor;
44import org.onosproject.net.packet.PacketService;
45import org.osgi.service.component.annotations.Activate;
46import org.osgi.service.component.annotations.Component;
47import org.osgi.service.component.annotations.Deactivate;
48import org.osgi.service.component.annotations.Reference;
49import org.osgi.service.component.annotations.ReferenceCardinality;
50import org.slf4j.Logger;
51
52import java.nio.ByteBuffer;
53import java.util.Objects;
54import java.util.Set;
55import java.util.concurrent.ExecutorService;
56import java.util.stream.Collectors;
57
58import static java.lang.Thread.sleep;
59import static java.util.concurrent.Executors.newSingleThreadExecutor;
60import static org.onlab.util.Tools.groupedThreads;
61import static org.onosproject.k8snetworking.api.Constants.EXT_ENTRY_TABLE;
62import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
Jian Li44c2b122019-05-03 14:46:34 +090063import static org.onosproject.k8snetworking.api.Constants.PRIORITY_ARP_POD_RULE;
Jian Li1b08d652019-05-02 17:28:09 +090064import static org.onosproject.k8snetworking.api.Constants.PRIORITY_ARP_REPLY_RULE;
Jian Lieab51352020-09-11 03:29:16 +090065import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.allK8sDevices;
Jian Li1b08d652019-05-02 17:28:09 +090066import static org.slf4j.LoggerFactory.getLogger;
67
68/**
69 * Handles ARP request/reply from external gateway.
70 */
71@Component(immediate = true)
72public class K8sRoutingArpHandler {
73 private final Logger log = getLogger(getClass());
74
75 private static final long SLEEP_MS = 5000;
76
77 @Reference(cardinality = ReferenceCardinality.MANDATORY)
78 protected CoreService coreService;
79
80 @Reference(cardinality = ReferenceCardinality.MANDATORY)
81 protected PacketService packetService;
82
83 @Reference(cardinality = ReferenceCardinality.MANDATORY)
84 protected ClusterService clusterService;
85
86 @Reference(cardinality = ReferenceCardinality.MANDATORY)
87 protected LeadershipService leadershipService;
88
89 @Reference(cardinality = ReferenceCardinality.MANDATORY)
90 protected K8sNodeAdminService k8sNodeService;
91
92 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Lieab51352020-09-11 03:29:16 +090093 protected K8sHostAdminService k8sHostService;
94
95 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Jian Li1b08d652019-05-02 17:28:09 +090096 protected K8sFlowRuleService k8sFlowRuleService;
97
98 private ApplicationId appId;
99 private NodeId localNodeId;
100
101 private final InternalK8sNodeListener k8sNodeListener = new InternalK8sNodeListener();
102 private final ExecutorService eventExecutor = newSingleThreadExecutor(
103 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
104
105 private final PacketProcessor packetProcessor = new InternalPacketProcessor();
106
107 @Activate
108 protected void activate() {
109 appId = coreService.registerApplication(K8S_NETWORKING_APP_ID);
110 localNodeId = clusterService.getLocalNode().id();
111 leadershipService.runForLeadership(appId.name());
112 k8sNodeService.addListener(k8sNodeListener);
113 packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
114 log.info("Started");
115 }
116
117 @Deactivate
118 protected void deactivate() {
119 k8sNodeService.removeListener(k8sNodeListener);
120 packetService.removeProcessor(packetProcessor);
121 leadershipService.withdraw(appId.name());
122 eventExecutor.shutdown();
123 log.info("Stopped");
124 }
125
126 private void processArpPacket(PacketContext context, Ethernet ethernet) {
Jian Lieab51352020-09-11 03:29:16 +0900127
128 DeviceId deviceId = context.inPacket().receivedFrom().deviceId();
129
130 if (!allK8sDevices(k8sNodeService, k8sHostService).contains(deviceId)) {
131 return;
132 }
133
Jian Li1b08d652019-05-02 17:28:09 +0900134 ARP arp = (ARP) ethernet.getPayload();
135
136 if (arp.getOpCode() == ARP.OP_REPLY) {
137 IpAddress spa = Ip4Address.valueOf(arp.getSenderProtocolAddress());
138 MacAddress sha = MacAddress.valueOf(arp.getSenderHardwareAddress());
139
Jian Li1efcb982020-02-04 00:32:21 +0900140 log.info("ARP reply from ip: {}, mac: {}", spa, sha);
Jian Li1b08d652019-05-02 17:28:09 +0900141
142 Set<IpAddress> gatewayIps = k8sNodeService.completeNodes().stream()
143 .map(K8sNode::extGatewayIp).collect(Collectors.toSet());
144
145 if (!gatewayIps.contains(spa)) {
146 return;
147 }
148
Jian Li1efcb982020-02-04 00:32:21 +0900149 log.info("ARP reply from external gateway ip: {}, mac: {}", spa, sha);
150
Jian Li1b08d652019-05-02 17:28:09 +0900151 k8sNodeService.completeNodes().stream()
152 .filter(n -> n.extGatewayMac() == null)
153 .forEach(n -> {
154 K8sNode updated = n.updateExtGatewayMac(sha);
155 k8sNodeService.updateNode(updated);
156 });
157 }
158 }
159
160 private void sendArpRequest(K8sNode k8sNode) {
161 MacAddress bridgeMac = k8sNode.extBridgeMac();
162 IpAddress bridgeIp = k8sNode.extBridgeIp();
163 IpAddress extGatewayIp = k8sNode.extGatewayIp();
164 Ethernet ethRequest = ARP.buildArpRequest(bridgeMac.toBytes(), bridgeIp.toOctets(),
165 extGatewayIp.toOctets(), VlanId.NO_VID);
166
167 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
168 .setOutput(k8sNode.extBridgePortNum())
169 .build();
170
171 packetService.emit(new DefaultOutboundPacket(
172 k8sNode.extBridge(),
173 treatment,
174 ByteBuffer.wrap(ethRequest.serialize())));
175 }
176
177 private void setArpReplyRule(K8sNode k8sNode, boolean install) {
178 TrafficSelector selector = DefaultTrafficSelector.builder()
179 .matchEthType(Ethernet.TYPE_ARP)
180 .matchArpOp(ARP.OP_REPLY)
181 .matchArpSpa(Ip4Address.valueOf(k8sNode.extGatewayIp().toString()))
182 .build();
183
184 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
185 .punt()
186 .build();
187
188 k8sFlowRuleService.setRule(
189 appId,
190 k8sNode.extBridge(),
191 selector,
192 treatment,
193 PRIORITY_ARP_REPLY_RULE,
194 EXT_ENTRY_TABLE,
195 install
196 );
197 }
198
Jian Li44c2b122019-05-03 14:46:34 +0900199 private void setPodArpRequestRule(K8sNode k8sNode, boolean install) {
Jian Li5abc9f02020-09-04 19:38:37 +0900200 if (k8sNode.extBridgePortNum() == null) {
201 log.warn("External bridge port is disabled.");
202 return;
203 }
204
Jian Li44c2b122019-05-03 14:46:34 +0900205 TrafficSelector selector = DefaultTrafficSelector.builder()
206 .matchInPort(k8sNode.extToIntgPatchPortNum())
207 .matchEthType(Ethernet.TYPE_ARP)
208 .matchArpOp(ARP.OP_REQUEST)
209 .build();
210
211 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
212 .setOutput(k8sNode.extBridgePortNum())
213 .build();
214
215 k8sFlowRuleService.setRule(
216 appId,
217 k8sNode.extBridge(),
218 selector,
219 treatment,
220 PRIORITY_ARP_POD_RULE,
221 EXT_ENTRY_TABLE,
222 install
223 );
224 }
225
226 private void setPodArpReplyRule(K8sNode k8sNode, boolean install) {
227 TrafficSelector selector = DefaultTrafficSelector.builder()
228 .matchInPort(k8sNode.extBridgePortNum())
229 .matchEthType(Ethernet.TYPE_ARP)
230 .matchArpOp(ARP.OP_REPLY)
231 .build();
232
233 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
234 .setOutput(k8sNode.extToIntgPatchPortNum())
235 .build();
236
237 k8sFlowRuleService.setRule(
238 appId,
239 k8sNode.extBridge(),
240 selector,
241 treatment,
242 PRIORITY_ARP_POD_RULE,
243 EXT_ENTRY_TABLE,
244 install
245 );
246 }
247
Jian Li1b08d652019-05-02 17:28:09 +0900248 private class InternalK8sNodeListener implements K8sNodeListener {
249
250 private boolean isRelevantHelper() {
251 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
252 }
253
254 @Override
255 public void event(K8sNodeEvent event) {
256 switch (event.type()) {
257 case K8S_NODE_COMPLETE:
258 eventExecutor.execute(() -> processNodeCompletion(event.subject()));
259 break;
260 case K8S_NODE_INCOMPLETE:
261 default:
262 break;
263 }
264 }
265
266 private void processNodeCompletion(K8sNode k8sNode) {
267 if (!isRelevantHelper()) {
268 return;
269 }
270
271 setArpReplyRule(k8sNode, true);
Jian Li44c2b122019-05-03 14:46:34 +0900272 setPodArpRequestRule(k8sNode, true);
273 setPodArpReplyRule(k8sNode, true);
Jian Li1b08d652019-05-02 17:28:09 +0900274
275 try {
276 sleep(SLEEP_MS);
277 } catch (InterruptedException e) {
278 log.error("Exception caused during ARP requesting...");
279 }
280
281 sendArpRequest(k8sNode);
282 }
283 }
284
285 private class InternalPacketProcessor implements PacketProcessor {
286
287 @Override
288 public void process(PacketContext context) {
289 if (context.isHandled()) {
290 return;
291 }
292
293 InboundPacket pkt = context.inPacket();
294 Ethernet ethernet = pkt.parsed();
295 if (ethernet != null && ethernet.getEtherType() == Ethernet.TYPE_ARP) {
296 eventExecutor.execute(() -> processArpPacket(context, ethernet));
297 }
298 }
299 }
300}