blob: 89680b0e209a90261c6c10bd56e0982c0f6ff036 [file] [log] [blame]
Jian Li4aa17642019-01-30 00:01:11 +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.EthType;
20import org.onlab.packet.Ethernet;
21import org.onlab.packet.Ip4Address;
22import org.onlab.packet.IpAddress;
23import org.onlab.packet.MacAddress;
24import org.onlab.util.Tools;
25import org.onosproject.cfg.ComponentConfigService;
26import org.onosproject.cfg.ConfigProperty;
27import org.onosproject.cluster.ClusterService;
28import org.onosproject.cluster.LeadershipService;
29import org.onosproject.cluster.NodeId;
30import org.onosproject.core.ApplicationId;
31import org.onosproject.core.CoreService;
32import org.onosproject.k8snetworking.api.K8sFlowRuleService;
33import org.onosproject.k8snetworking.api.K8sNetworkService;
34import org.onosproject.k8snetworking.api.K8sPort;
Jian Li7d111d72019-04-12 13:58:44 +090035import org.onosproject.k8snetworking.api.K8sServiceService;
Jian Li4aa17642019-01-30 00:01:11 +090036import org.onosproject.k8snode.api.K8sNode;
37import org.onosproject.k8snode.api.K8sNodeEvent;
38import org.onosproject.k8snode.api.K8sNodeListener;
39import org.onosproject.k8snode.api.K8sNodeService;
40import org.onosproject.mastership.MastershipService;
41import org.onosproject.net.PortNumber;
42import org.onosproject.net.device.DeviceService;
43import org.onosproject.net.flow.DefaultTrafficSelector;
44import org.onosproject.net.flow.DefaultTrafficTreatment;
45import org.onosproject.net.flow.TrafficSelector;
46import org.onosproject.net.flow.TrafficTreatment;
47import org.onosproject.net.packet.DefaultOutboundPacket;
48import org.onosproject.net.packet.PacketContext;
49import org.onosproject.net.packet.PacketProcessor;
50import org.onosproject.net.packet.PacketService;
51import org.osgi.service.component.ComponentContext;
52import org.osgi.service.component.annotations.Activate;
53import org.osgi.service.component.annotations.Component;
54import org.osgi.service.component.annotations.Deactivate;
55import org.osgi.service.component.annotations.Modified;
56import org.osgi.service.component.annotations.Reference;
57import org.osgi.service.component.annotations.ReferenceCardinality;
58import org.slf4j.Logger;
59import org.slf4j.LoggerFactory;
60
61import java.nio.ByteBuffer;
62import java.util.Dictionary;
63import java.util.Objects;
64import java.util.Set;
65import java.util.concurrent.ExecutorService;
Jian Li7d111d72019-04-12 13:58:44 +090066import java.util.stream.Collectors;
Jian Li4aa17642019-01-30 00:01:11 +090067
68import static java.util.concurrent.Executors.newSingleThreadExecutor;
69import static org.onlab.util.Tools.groupedThreads;
70import static org.onosproject.k8snetworking.api.Constants.ARP_BROADCAST_MODE;
71import static org.onosproject.k8snetworking.api.Constants.ARP_PROXY_MODE;
72import static org.onosproject.k8snetworking.api.Constants.ARP_TABLE;
73import static org.onosproject.k8snetworking.api.Constants.K8S_NETWORKING_APP_ID;
74import static org.onosproject.k8snetworking.api.Constants.PRIORITY_ARP_CONTROL_RULE;
Jian Li7d111d72019-04-12 13:58:44 +090075import static org.onosproject.k8snetworking.api.Constants.SERVICE_FAKE_MAC_STR;
Jian Li4aa17642019-01-30 00:01:11 +090076import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.ARP_MODE;
77import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.ARP_MODE_DEFAULT;
78import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.GATEWAY_MAC;
79import static org.onosproject.k8snetworking.impl.OsgiPropertyConstants.GATEWAY_MAC_DEFAULT;
80import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.getPropertyValue;
Jian Li004526d2019-02-25 16:26:27 +090081import static org.onosproject.k8snetworking.util.K8sNetworkingUtil.unshiftIpDomain;
Jian Li4aa17642019-01-30 00:01:11 +090082
83/**
84 * Handles ARP packet from containers.
85 */
86@Component(
87 immediate = true,
88 property = {
89 GATEWAY_MAC + "=" + GATEWAY_MAC_DEFAULT,
90 ARP_MODE + "=" + ARP_MODE_DEFAULT
91 }
92)
93public class K8sSwitchingArpHandler {
94
95 private final Logger log = LoggerFactory.getLogger(getClass());
96
Jian Lid89db462019-02-08 18:21:57 +090097 private static final String API_SERVER_CLUSTER_IP = "10.96.0.1";
98
Jian Li7d111d72019-04-12 13:58:44 +090099 private static final String GATEWAY_MAC = "gatewayMac";
100 private static final String ARP_MODE = "arpMode";
101
Jian Li4aa17642019-01-30 00:01:11 +0900102 @Reference(cardinality = ReferenceCardinality.MANDATORY)
103 protected CoreService coreService;
104
105 @Reference(cardinality = ReferenceCardinality.MANDATORY)
106 protected PacketService packetService;
107
108 @Reference(cardinality = ReferenceCardinality.MANDATORY)
109 protected ComponentConfigService configService;
110
111 @Reference(cardinality = ReferenceCardinality.MANDATORY)
112 protected ClusterService clusterService;
113
114 @Reference(cardinality = ReferenceCardinality.MANDATORY)
115 protected LeadershipService leadershipService;
116
117 @Reference(cardinality = ReferenceCardinality.MANDATORY)
118 protected DeviceService deviceService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY)
121 protected MastershipService mastershipService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY)
124 protected K8sNodeService k8sNodeService;
125
126 @Reference(cardinality = ReferenceCardinality.MANDATORY)
127 protected K8sNetworkService k8sNetworkService;
128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY)
130 protected K8sFlowRuleService k8sFlowRuleService;
131
Jian Li7d111d72019-04-12 13:58:44 +0900132 @Reference(cardinality = ReferenceCardinality.MANDATORY)
133 protected K8sServiceService k8sServiceService;
134
Jian Li4aa17642019-01-30 00:01:11 +0900135 /** Fake MAC address for virtual network subnet gateway. */
136 private String gatewayMac = GATEWAY_MAC_DEFAULT;
137
138 /** ARP processing mode, broadcast | proxy (default). */
139 protected String arpMode = ARP_MODE_DEFAULT;
140
141 private MacAddress gwMacAddress;
142
143 private final ExecutorService eventExecutor = newSingleThreadExecutor(
144 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
145
146 private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
147 private final InternalNodeEventListener k8sNodeListener = new InternalNodeEventListener();
148
149 private ApplicationId appId;
150 private NodeId localNodeId;
151
152 @Activate
153 void activate() {
154 appId = coreService.registerApplication(K8S_NETWORKING_APP_ID);
155 configService.registerProperties(getClass());
156 localNodeId = clusterService.getLocalNode().id();
157 leadershipService.runForLeadership(appId.name());
158 k8sNodeService.addListener(k8sNodeListener);
159 packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
160
161 log.info("Started");
162 }
163
164 @Deactivate
165 void deactivate() {
166 packetService.removeProcessor(packetProcessor);
167 k8sNodeService.removeListener(k8sNodeListener);
168 leadershipService.withdraw(appId.name());
169 configService.unregisterProperties(getClass(), false);
170 eventExecutor.shutdown();
171
172 log.info("Stopped");
173 }
174
175 @Modified
176 void modified(ComponentContext context) {
177 readComponentConfiguration(context);
178
179 log.info("Modified");
180 }
181
182 /**
183 * Processes ARP request packets.
184 *
185 * @param context packet context
186 * @param ethPacket ethernet packet
187 */
188 private void processPacketIn(PacketContext context, Ethernet ethPacket) {
189 // if the ARP mode is configured as broadcast mode, we simply ignore ARP packet_in
190 if (ARP_BROADCAST_MODE.equals(getArpMode())) {
191 return;
192 }
193
194 ARP arpPacket = (ARP) ethPacket.getPayload();
195 if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
196 return;
197 }
198
199 K8sPort srcPort = k8sNetworkService.ports().stream()
200 .filter(p -> p.macAddress().equals(ethPacket.getSourceMAC()))
201 .findAny().orElse(null);
202
203 if (srcPort == null && !context.inPacket().receivedFrom().port()
204 .equals(PortNumber.LOCAL)) {
205 log.warn("Failed to find source port(MAC:{})", ethPacket.getSourceMAC());
206 return;
207 }
208
209 // FIXME: this is a workaround for storing host GW MAC address,
210 // need to find a way to store the MAC address in persistent way
211 if (context.inPacket().receivedFrom().port().equals(PortNumber.LOCAL)) {
212 gwMacAddress = ethPacket.getSourceMAC();
213 }
214
215 IpAddress targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
216
217 MacAddress replyMac = k8sNetworkService.ports().stream()
218 // .filter(p -> p.networkId().equals(srcPort.networkId()))
219 .filter(p -> p.ipAddress().equals(targetIp))
220 .map(K8sPort::macAddress)
221 .findAny().orElse(null);
222
223 long gwIpCnt = k8sNetworkService.networks().stream()
224 .filter(n -> n.gatewayIp().equals(targetIp))
225 .count();
226
Jian Li7d111d72019-04-12 13:58:44 +0900227 if (gwIpCnt > 0) {
Jian Li4aa17642019-01-30 00:01:11 +0900228 replyMac = gwMacAddress;
229 }
230
231 if (replyMac == null) {
Jian Li004526d2019-02-25 16:26:27 +0900232 Set<String> unshiftedIps = unshiftIpDomain(targetIp.toString(), k8sNetworkService);
233 for (String ip : unshiftedIps) {
234 replyMac = k8sNetworkService.ports().stream()
235 .filter(p -> p.ipAddress().equals(IpAddress.valueOf(ip)))
236 .map(K8sPort::macAddress)
237 .findAny().orElse(null);
238
239 if (replyMac != null) {
240 break;
241 }
242 }
243 }
244
245 if (replyMac == null) {
Jian Li7d111d72019-04-12 13:58:44 +0900246 Set<String> serviceIps = k8sServiceService.services().stream()
247 .map(s -> s.getSpec().getClusterIP())
248 .collect(Collectors.toSet());
249 if (serviceIps.contains(targetIp.toString())) {
250 replyMac = MacAddress.valueOf(SERVICE_FAKE_MAC_STR);
251 }
252 }
253
254 if (replyMac == null) {
Jian Li4aa17642019-01-30 00:01:11 +0900255 log.debug("Failed to find MAC address for {}", targetIp);
256 return;
257 }
258
259 Ethernet ethReply = ARP.buildArpReply(
260 targetIp.getIp4Address(),
261 replyMac,
262 ethPacket);
263
264 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
265 .setOutput(context.inPacket().receivedFrom().port())
266 .build();
267
268 packetService.emit(new DefaultOutboundPacket(
269 context.inPacket().receivedFrom().deviceId(),
270 treatment,
271 ByteBuffer.wrap(ethReply.serialize())));
272 }
273
274 private String getArpMode() {
275 Set<ConfigProperty> properties = configService.getProperties(this.getClass().getName());
276 return getPropertyValue(properties, ARP_MODE);
277 }
278
279 /**
280 * Extracts properties from the component configuration context.
281 *
282 * @param context the component context
283 */
284 private void readComponentConfiguration(ComponentContext context) {
285 Dictionary<?, ?> properties = context.getProperties();
286
287 String updatedMac = Tools.get(properties, GATEWAY_MAC);
288 gatewayMac = updatedMac != null ? updatedMac : GATEWAY_MAC_DEFAULT;
289 log.info("Configured. Gateway MAC is {}", gatewayMac);
290 }
291
292 /**
293 * An internal packet processor which processes ARP request, and results in
294 * packet-out ARP reply.
295 */
296 private class InternalPacketProcessor implements PacketProcessor {
297
298 @Override
299 public void process(PacketContext context) {
300 if (context.isHandled()) {
301 return;
302 }
303
304 Ethernet ethPacket = context.inPacket().parsed();
305 if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
306 return;
307 }
308
309 eventExecutor.execute(() -> processPacketIn(context, ethPacket));
310 }
311 }
312
313 /**
314 * An internal kubernetes node listener which is used for listening kubernetes
315 * node activity. As long as a node is in complete state, we will install
316 * default ARP rule to handle ARP request.
317 */
318 private class InternalNodeEventListener implements K8sNodeListener {
319
320 private boolean isRelevantHelper() {
321 return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
322 }
323
324 @Override
325 public void event(K8sNodeEvent event) {
326 K8sNode k8sNode = event.subject();
327 switch (event.type()) {
328 case K8S_NODE_COMPLETE:
329 eventExecutor.execute(() -> processNodeCompletion(k8sNode));
330 break;
331 case K8S_NODE_INCOMPLETE:
332 eventExecutor.execute(() -> processNodeIncompletion(k8sNode));
333 break;
334 default:
335 break;
336 }
337 }
338
339 private void processNodeCompletion(K8sNode node) {
340 if (!isRelevantHelper()) {
341 return;
342 }
343
344 setDefaultArpRule(node, true);
345 }
346
347 private void processNodeIncompletion(K8sNode node) {
348 if (!isRelevantHelper()) {
349 return;
350 }
351
352 setDefaultArpRule(node, false);
353 }
354
355 private void setDefaultArpRule(K8sNode node, boolean install) {
356
357 if (getArpMode() == null) {
358 return;
359 }
360
361 switch (getArpMode()) {
362 case ARP_PROXY_MODE:
363 setDefaultArpRuleForProxyMode(node, install);
364 break;
365 case ARP_BROADCAST_MODE:
366 // TODO: need to implement broadcast mode
367 log.warn("Not implemented yet.");
368 break;
369 default:
370 log.warn("Invalid ARP mode {}. Please use either " +
371 "broadcast or proxy mode.", getArpMode());
372 break;
373 }
374 }
375
376 private void setDefaultArpRuleForProxyMode(K8sNode node, boolean install) {
377 TrafficSelector selector = DefaultTrafficSelector.builder()
378 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
379 .build();
380
381 TrafficTreatment treatment = DefaultTrafficTreatment.builder()
382 .punt()
383 .build();
384
385 k8sFlowRuleService.setRule(
386 appId,
387 node.intgBridge(),
388 selector,
389 treatment,
390 PRIORITY_ARP_CONTROL_RULE,
391 ARP_TABLE,
392 install
393 );
394 }
395 }
396}