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