blob: fd16e1a2e5f178ba68461718c0c8e17f5d87ff80 [file] [log] [blame]
gauravf0884562016-06-17 02:47:13 +05301/*
2 * Copyright 2016-present Open Networking Laboratory
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.dhcprelay;
17
gaurav4af95fb2016-07-07 01:48:44 +053018import java.nio.ByteBuffer;
Pier Luigi20557c82017-01-12 22:40:46 -080019import java.util.Dictionary;
gaurav38351de2016-07-27 04:44:33 +053020import java.util.Objects;
gauravf0884562016-06-17 02:47:13 +053021import java.util.Set;
gauravf0884562016-06-17 02:47:13 +053022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
Pier Luigi20557c82017-01-12 22:40:46 -080025import org.apache.felix.scr.annotations.Modified;
26import org.apache.felix.scr.annotations.Property;
gauravf0884562016-06-17 02:47:13 +053027import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
gaurav38351de2016-07-27 04:44:33 +053029import org.onlab.packet.ARP;
gauravf0884562016-06-17 02:47:13 +053030import org.onlab.packet.DHCP;
gaurav4af95fb2016-07-07 01:48:44 +053031import org.onlab.packet.DHCPOption;
32import org.onlab.packet.DHCPPacketType;
gauravf0884562016-06-17 02:47:13 +053033import org.onlab.packet.Ethernet;
34import org.onlab.packet.IPv4;
gaurav38351de2016-07-27 04:44:33 +053035import org.onlab.packet.Ip4Address;
36import org.onlab.packet.MacAddress;
gauravf0884562016-06-17 02:47:13 +053037import org.onlab.packet.TpPort;
38import org.onlab.packet.UDP;
gaurav4af95fb2016-07-07 01:48:44 +053039import org.onlab.packet.VlanId;
Pier Luigi20557c82017-01-12 22:40:46 -080040import org.onlab.util.Tools;
gauravf0884562016-06-17 02:47:13 +053041import org.onosproject.core.ApplicationId;
42import org.onosproject.core.CoreService;
gaurav38351de2016-07-27 04:44:33 +053043import org.onosproject.incubator.net.intf.Interface;
44import org.onosproject.incubator.net.intf.InterfaceService;
gauravf0884562016-06-17 02:47:13 +053045import org.onosproject.net.ConnectPoint;
46import org.onosproject.net.Host;
47import org.onosproject.net.HostId;
48import org.onosproject.net.config.ConfigFactory;
49import org.onosproject.net.config.NetworkConfigEvent;
50import org.onosproject.net.config.NetworkConfigListener;
51import org.onosproject.net.config.NetworkConfigRegistry;
52import org.onosproject.net.flow.DefaultTrafficSelector;
53import org.onosproject.net.flow.DefaultTrafficTreatment;
54import org.onosproject.net.flow.TrafficSelector;
55import org.onosproject.net.flow.TrafficTreatment;
Saurav Dasb0ae6ee2017-03-04 16:08:47 -080056import org.onosproject.net.host.HostEvent;
57import org.onosproject.net.host.HostListener;
gauravf0884562016-06-17 02:47:13 +053058import org.onosproject.net.host.HostService;
Saurav Dasb0ae6ee2017-03-04 16:08:47 -080059import org.onosproject.net.host.InterfaceIpAddress;
gauravf0884562016-06-17 02:47:13 +053060import org.onosproject.net.packet.DefaultOutboundPacket;
61import org.onosproject.net.packet.OutboundPacket;
62import org.onosproject.net.packet.PacketContext;
63import org.onosproject.net.packet.PacketPriority;
64import org.onosproject.net.packet.PacketProcessor;
65import org.onosproject.net.packet.PacketService;
Pier Luigi20557c82017-01-12 22:40:46 -080066import org.osgi.service.component.ComponentContext;
gauravf0884562016-06-17 02:47:13 +053067import org.slf4j.Logger;
68import org.slf4j.LoggerFactory;
69
70import com.google.common.collect.ImmutableSet;
gaurav38351de2016-07-27 04:44:33 +053071
gauravf0884562016-06-17 02:47:13 +053072import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
gaurav4af95fb2016-07-07 01:48:44 +053073import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
gaurav38351de2016-07-27 04:44:33 +053074import static org.onlab.packet.MacAddress.valueOf;
gauravf0884562016-06-17 02:47:13 +053075/**
76 * DHCP Relay Agent Application Component.
77 */
78@Component(immediate = true)
79public class DhcpRelay {
80
81 public static final String DHCP_RELAY_APP = "org.onosproject.dhcp-relay";
82 private final Logger log = LoggerFactory.getLogger(getClass());
83 private final InternalConfigListener cfgListener = new InternalConfigListener();
gaurav38351de2016-07-27 04:44:33 +053084 private static MacAddress myMAC = valueOf("4f:4f:4f:4f:4f:4f");
gauravf0884562016-06-17 02:47:13 +053085
86 private final Set<ConfigFactory> factories = ImmutableSet.of(
87 new ConfigFactory<ApplicationId, DhcpRelayConfig>(APP_SUBJECT_FACTORY,
88 DhcpRelayConfig.class,
89 "dhcprelay") {
90 @Override
91 public DhcpRelayConfig createConfig() {
92 return new DhcpRelayConfig();
93 }
94 }
95 );
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected NetworkConfigRegistry cfgService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected CoreService coreService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected PacketService packetService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected HostService hostService;
108
gaurav38351de2016-07-27 04:44:33 +0530109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected InterfaceService interfaceService;
111
Pier Luigi20557c82017-01-12 22:40:46 -0800112 @Property(name = "arpEnabled", boolValue = true,
113 label = "Enable Address resolution protocol")
114 protected boolean arpEnabled = true;
115
gauravf0884562016-06-17 02:47:13 +0530116 private DhcpRelayPacketProcessor dhcpRelayPacketProcessor = new DhcpRelayPacketProcessor();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800117 private InternalHostListener hostListener = new InternalHostListener();
118
gaurav38351de2016-07-27 04:44:33 +0530119 private Ip4Address dhcpServerIp = null;
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800120 // dhcp server may be connected directly to the SDN network or
121 // via an external gateway. When connected directly, the dhcpConnectPoint, dhcpConnectMac,
122 // and dhcpConnectVlan refer to the server. When connected via the gateway, they refer
123 // to the gateway.
124 private ConnectPoint dhcpServerConnectPoint = null;
125 private MacAddress dhcpConnectMac = null;
126 private VlanId dhcpConnectVlan = null;
127 private Ip4Address dhcpGatewayIp = null;
gauravf0884562016-06-17 02:47:13 +0530128 private ApplicationId appId;
129
130 @Activate
Pier Luigi20557c82017-01-12 22:40:46 -0800131 protected void activate(ComponentContext context) {
gauravf0884562016-06-17 02:47:13 +0530132 //start the dhcp relay agent
gauravf0884562016-06-17 02:47:13 +0530133 appId = coreService.registerApplication(DHCP_RELAY_APP);
134
135 cfgService.addListener(cfgListener);
136 factories.forEach(cfgService::registerConfigFactory);
137 //update the dhcp server configuration.
138 updateConfig();
139 //add the packet services.
140 packetService.addProcessor(dhcpRelayPacketProcessor, PacketProcessor.director(0));
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800141 hostService.addListener(hostListener);
Pier Luigi20557c82017-01-12 22:40:46 -0800142 requestDhcpPackets();
143 modified(context);
144
gauravf0884562016-06-17 02:47:13 +0530145 log.info("DHCP-RELAY Started");
gauravf0884562016-06-17 02:47:13 +0530146 }
147
148 @Deactivate
149 protected void deactivate() {
150 cfgService.removeListener(cfgListener);
151 factories.forEach(cfgService::unregisterConfigFactory);
152 packetService.removeProcessor(dhcpRelayPacketProcessor);
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800153 hostService.removeListener(hostListener);
Pier Luigi20557c82017-01-12 22:40:46 -0800154 cancelDhcpPackets();
155 cancelArpPackets();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800156 if (dhcpGatewayIp != null) {
157 hostService.stopMonitoringIp(dhcpGatewayIp);
158 } else {
159 hostService.stopMonitoringIp(dhcpServerIp);
160 }
Pier Luigi20557c82017-01-12 22:40:46 -0800161
gauravf0884562016-06-17 02:47:13 +0530162 log.info("DHCP-RELAY Stopped");
163 }
164
Pier Luigi20557c82017-01-12 22:40:46 -0800165 @Modified
166 protected void modified(ComponentContext context) {
167 Dictionary<?, ?> properties = context.getProperties();
168 Boolean flag;
169
170 flag = Tools.isPropertyEnabled(properties, "arpEnabled");
171 if (flag != null) {
172 arpEnabled = flag;
173 log.info("Address resolution protocol is {}",
174 arpEnabled ? "enabled" : "disabled");
175 }
176
177 if (arpEnabled) {
178 requestArpPackets();
179 } else {
180 cancelArpPackets();
181 }
182 }
183
Charles Chan3c1691a2017-02-16 14:55:22 -0800184 /**
185 * Checks if this app has been configured.
186 *
187 * @return true if all information we need have been initialized
188 */
189 private boolean configured() {
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800190 return dhcpServerConnectPoint != null && dhcpServerIp != null;
Charles Chan3c1691a2017-02-16 14:55:22 -0800191 }
192
gauravf0884562016-06-17 02:47:13 +0530193 private void updateConfig() {
194 DhcpRelayConfig cfg = cfgService.getConfig(appId, DhcpRelayConfig.class);
gauravf0884562016-06-17 02:47:13 +0530195 if (cfg == null) {
196 log.warn("Dhcp Server info not available");
197 return;
198 }
199
200 dhcpServerConnectPoint = cfg.getDhcpServerConnectPoint();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800201 Ip4Address oldDhcpServerIp = dhcpServerIp;
202 Ip4Address oldDhcpGatewayIp = dhcpGatewayIp;
gaurav38351de2016-07-27 04:44:33 +0530203 dhcpServerIp = cfg.getDhcpServerIp();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800204 dhcpGatewayIp = cfg.getDhcpGatewayIp();
205 dhcpConnectMac = null; // reset for updated config
206 dhcpConnectVlan = null; // reset for updated config
207 log.info("dhcp server connect point: " + dhcpServerConnectPoint);
gaurav38351de2016-07-27 04:44:33 +0530208 log.info("dhcp server ipaddress " + dhcpServerIp);
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800209 if (dhcpGatewayIp != null) {
210 // check for gateway
211 Set<Host> ghosts = hostService.getHostsByIp(dhcpGatewayIp);
212 if (ghosts == null || ghosts.isEmpty()) {
213 log.info("Probing to resolve dhcp gateway IP {}", dhcpGatewayIp);
214 if (oldDhcpGatewayIp != null) {
215 hostService.stopMonitoringIp(oldDhcpGatewayIp);
216 }
217 hostService.startMonitoringIp(dhcpGatewayIp);
218 } else {
219 // gw is known, no need to probe; should be only 1 host with this ip
220 hostUpdated(ghosts.iterator().next());
221 }
222 } else {
223 // check for server
224 Set<Host> shosts = hostService.getHostsByIp(dhcpServerIp);
225 if (shosts == null || shosts.isEmpty()) {
226 log.info("Probing to resolve dhcp server IP {}", dhcpServerIp);
227 if (oldDhcpServerIp != null) {
228 hostService.stopMonitoringIp(oldDhcpServerIp);
229 }
230 hostService.startMonitoringIp(dhcpServerIp);
231 } else {
232 // dhcp server is know, no need to probe
233 hostUpdated(shosts.iterator().next());
234 }
235 }
236 }
237
238 private void hostRemoved(Host host) {
239 if (host.ipAddresses().contains(dhcpServerIp)) {
240 log.warn("DHCP server {} removed", dhcpServerIp);
241 dhcpConnectMac = null;
242 dhcpConnectVlan = null;
243 }
244 if (dhcpGatewayIp != null && host.ipAddresses().contains(dhcpGatewayIp)) {
245 log.warn("DHCP gateway {} removed", dhcpGatewayIp);
246 dhcpConnectMac = null;
247 dhcpConnectVlan = null;
248 }
249 }
250
251 private void hostUpdated(Host host) {
252 if (dhcpGatewayIp != null) {
253 if (host.ipAddresses().contains(dhcpGatewayIp)) {
254 dhcpConnectMac = host.mac();
255 dhcpConnectVlan = host.vlan();
256 log.info("DHCP gateway {} resolved to Mac/Vlan:{}/{}", dhcpGatewayIp,
257 dhcpConnectMac, dhcpConnectVlan);
258 }
259 return;
260 }
261 if (host.ipAddresses().contains(dhcpServerIp)) {
262 dhcpConnectMac = host.mac();
263 dhcpConnectVlan = host.vlan();
264 log.info("DHCP server {} resolved to Mac/Vlan:{}/{}", dhcpServerIp,
265 dhcpConnectMac, dhcpConnectVlan);
266 }
gauravf0884562016-06-17 02:47:13 +0530267 }
268
269 /**
Pier Luigi20557c82017-01-12 22:40:46 -0800270 * Request DHCP packet in via PacketService.
gauravf0884562016-06-17 02:47:13 +0530271 */
Pier Luigi20557c82017-01-12 22:40:46 -0800272 private void requestDhcpPackets() {
gauravf0884562016-06-17 02:47:13 +0530273 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
274 .matchEthType(Ethernet.TYPE_IPV4)
275 .matchIPProtocol(IPv4.PROTOCOL_UDP)
gaurav38351de2016-07-27 04:44:33 +0530276 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
gauravf0884562016-06-17 02:47:13 +0530277 packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
278
279 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
280 .matchEthType(Ethernet.TYPE_IPV4)
281 .matchIPProtocol(IPv4.PROTOCOL_UDP)
gaurav38351de2016-07-27 04:44:33 +0530282 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
gauravf0884562016-06-17 02:47:13 +0530283 packetService.requestPackets(selectorClient.build(), PacketPriority.CONTROL, appId);
284 }
285
286 /**
Pier Luigi20557c82017-01-12 22:40:46 -0800287 * Cancel requested DHCP packets in via packet service.
gauravf0884562016-06-17 02:47:13 +0530288 */
Pier Luigi20557c82017-01-12 22:40:46 -0800289 private void cancelDhcpPackets() {
gauravf0884562016-06-17 02:47:13 +0530290 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
291 .matchEthType(Ethernet.TYPE_IPV4)
292 .matchIPProtocol(IPv4.PROTOCOL_UDP)
gaurav38351de2016-07-27 04:44:33 +0530293 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
gauravf0884562016-06-17 02:47:13 +0530294 packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
295
296 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
297 .matchEthType(Ethernet.TYPE_IPV4)
298 .matchIPProtocol(IPv4.PROTOCOL_UDP)
gaurav38351de2016-07-27 04:44:33 +0530299 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
gauravf0884562016-06-17 02:47:13 +0530300 packetService.cancelPackets(selectorClient.build(), PacketPriority.CONTROL, appId);
Pier Luigi20557c82017-01-12 22:40:46 -0800301 }
gaurav38351de2016-07-27 04:44:33 +0530302
Pier Luigi20557c82017-01-12 22:40:46 -0800303 /**
304 * Request ARP packet in via PacketService.
305 */
306 private void requestArpPackets() {
307 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
308 .matchEthType(Ethernet.TYPE_ARP);
309 packetService.requestPackets(selectorArpServer.build(), PacketPriority.CONTROL, appId);
310 }
311
312 /**
313 * Cancel requested ARP packets in via packet service.
314 */
315 private void cancelArpPackets() {
gaurav38351de2016-07-27 04:44:33 +0530316 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
317 .matchEthType(Ethernet.TYPE_ARP);
318 packetService.cancelPackets(selectorArpServer.build(), PacketPriority.CONTROL, appId);
gauravf0884562016-06-17 02:47:13 +0530319 }
320
321 private class DhcpRelayPacketProcessor implements PacketProcessor {
322
323 @Override
324 public void process(PacketContext context) {
Charles Chan3c1691a2017-02-16 14:55:22 -0800325 if (!configured()) {
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800326 log.warn("Missing DHCP relay server config. Abort packet processing");
Charles Chan3c1691a2017-02-16 14:55:22 -0800327 return;
328 }
329
gauravf0884562016-06-17 02:47:13 +0530330 // process the packet and get the payload
331 Ethernet packet = context.inPacket().parsed();
gauravf0884562016-06-17 02:47:13 +0530332 if (packet == null) {
333 return;
334 }
335
336 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
337 IPv4 ipv4Packet = (IPv4) packet.getPayload();
338
339 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
340 UDP udpPacket = (UDP) ipv4Packet.getPayload();
341 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
gaurav38351de2016-07-27 04:44:33 +0530342 if (udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT ||
343 udpPacket.getSourcePort() == UDP.DHCP_SERVER_PORT) {
gaurav4af95fb2016-07-07 01:48:44 +0530344 //This packet is dhcp.
345 processDhcpPacket(context, dhcpPayload);
gauravf0884562016-06-17 02:47:13 +0530346 }
347 }
Pier Luigi20557c82017-01-12 22:40:46 -0800348 } else if (packet.getEtherType() == Ethernet.TYPE_ARP && arpEnabled) {
gaurav38351de2016-07-27 04:44:33 +0530349 ARP arpPacket = (ARP) packet.getPayload();
350 Set<Interface> serverInterfaces = interfaceService.
351 getInterfacesByPort(context.inPacket().receivedFrom());
352 //ignore the packets if dhcp server interface is not configured on onos.
353 if (serverInterfaces.isEmpty()) {
354 log.warn("server virtual interface not configured");
355 return;
356 }
357 if ((arpPacket.getOpCode() == ARP.OP_REQUEST) &&
358 checkArpRequestFrmDhcpServ(serverInterfaces, arpPacket)) {
359 processArpPacket(context, packet);
360 }
gauravf0884562016-06-17 02:47:13 +0530361 }
362 }
363
gaurav38351de2016-07-27 04:44:33 +0530364 //method to check the arp request is from dhcp server for default-gateway.
365 private boolean checkArpRequestFrmDhcpServ(Set<Interface> serverInterfaces, ARP arpPacket) {
366 if (Objects.equals(serverInterfaces.iterator().next().ipAddressesList().get(0).
367 ipAddress().getIp4Address(),
368 Ip4Address.valueOf(arpPacket.getTargetProtocolAddress()))) {
369 return true;
370 }
371 return false;
372 }
373
gauravf0884562016-06-17 02:47:13 +0530374 //forward the packet to ConnectPoint where the DHCP server is attached.
gaurav4af95fb2016-07-07 01:48:44 +0530375 private void forwardPacket(Ethernet packet) {
gauravf0884562016-06-17 02:47:13 +0530376 //send Packetout to dhcp server connectpoint.
377 if (dhcpServerConnectPoint != null) {
378 TrafficTreatment t = DefaultTrafficTreatment.builder()
379 .setOutput(dhcpServerConnectPoint.port()).build();
380 OutboundPacket o = new DefaultOutboundPacket(
gaurav4af95fb2016-07-07 01:48:44 +0530381 dhcpServerConnectPoint.deviceId(), t, ByteBuffer.wrap(packet.serialize()));
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800382 if (log.isTraceEnabled()) {
383 log.trace("Relaying packet to dhcp server {}", packet);
384 }
gauravf0884562016-06-17 02:47:13 +0530385 packetService.emit(o);
386 }
387 }
388
gaurav38351de2016-07-27 04:44:33 +0530389 /**
390 * Processes the ARP Payload and initiates a reply to the client.
391 *
392 * @param context context of the incoming message
393 * @param packet the ethernet payload
394 */
395 private void processArpPacket(PacketContext context, Ethernet packet) {
gaurav38351de2016-07-27 04:44:33 +0530396 ARP arpPacket = (ARP) packet.getPayload();
397
398 ARP arpReply = (ARP) arpPacket.clone();
399 arpReply.setOpCode(ARP.OP_REPLY);
400
401 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
402 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
403 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
404 arpReply.setSenderHardwareAddress(myMAC.toBytes());
405
406 // Ethernet Frame.
407 Ethernet ethReply = new Ethernet();
408 ethReply.setSourceMACAddress(myMAC);
409 ethReply.setDestinationMACAddress(packet.getSourceMAC());
410 ethReply.setEtherType(Ethernet.TYPE_ARP);
411 ethReply.setVlanID(packet.getVlanID());
412
413 ethReply.setPayload(arpReply);
414 forwardPacket(ethReply);
415 }
416
gaurav4af95fb2016-07-07 01:48:44 +0530417 //process the dhcp packet before sending to server
gauravf0884562016-06-17 02:47:13 +0530418 private void processDhcpPacket(PacketContext context, DHCP dhcpPayload) {
Charles Chan1b201232016-12-07 15:39:22 -0800419 ConnectPoint inPort = context.inPacket().receivedFrom();
420 Set<Interface> clientServerInterfaces = interfaceService.getInterfacesByPort(inPort);
gaurav38351de2016-07-27 04:44:33 +0530421 //ignore the packets if dhcp client interface is not configured on onos.
422 if (clientServerInterfaces.isEmpty()) {
Charles Chan1b201232016-12-07 15:39:22 -0800423 log.warn("Virtual interface is not configured on {}", inPort);
gaurav38351de2016-07-27 04:44:33 +0530424 return;
425 }
426
gauravf0884562016-06-17 02:47:13 +0530427 if (dhcpPayload == null) {
428 return;
429 }
gaurav4af95fb2016-07-07 01:48:44 +0530430
gauravf0884562016-06-17 02:47:13 +0530431 Ethernet packet = context.inPacket().parsed();
432 DHCPPacketType incomingPacketType = null;
433 for (DHCPOption option : dhcpPayload.getOptions()) {
434 if (option.getCode() == OptionCode_MessageType.getValue()) {
435 byte[] data = option.getData();
436 incomingPacketType = DHCPPacketType.getType(data[0]);
437 }
438 }
gaurav38351de2016-07-27 04:44:33 +0530439
gauravf0884562016-06-17 02:47:13 +0530440 switch (incomingPacketType) {
441 case DHCPDISCOVER:
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800442 // add the gatewayip as virtual interface ip for server to understand
443 // the lease to be assigned and forward the packet to dhcp server.
444 Ethernet ethernetPacketDiscover =
445 processDhcpPacketFromClient(context, packet, clientServerInterfaces);
Charles Chan3c1691a2017-02-16 14:55:22 -0800446 if (ethernetPacketDiscover != null) {
447 forwardPacket(ethernetPacketDiscover);
448 }
gaurav4af95fb2016-07-07 01:48:44 +0530449 break;
450 case DHCPOFFER:
451 //reply to dhcp client.
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800452 Ethernet ethernetPacketOffer = processDhcpPacketFromServer(packet);
Charles Chan3c1691a2017-02-16 14:55:22 -0800453 if (ethernetPacketOffer != null) {
454 sendReply(ethernetPacketOffer, dhcpPayload);
455 }
gaurav4af95fb2016-07-07 01:48:44 +0530456 break;
457 case DHCPREQUEST:
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800458 // add the gatewayip as virtual interface ip for server to understand
459 // the lease to be assigned and forward the packet to dhcp server.
460 Ethernet ethernetPacketRequest =
461 processDhcpPacketFromClient(context, packet, clientServerInterfaces);
Charles Chan3c1691a2017-02-16 14:55:22 -0800462 if (ethernetPacketRequest != null) {
463 forwardPacket(ethernetPacketRequest);
464 }
gaurav4af95fb2016-07-07 01:48:44 +0530465 break;
466 case DHCPACK:
467 //reply to dhcp client.
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800468 Ethernet ethernetPacketAck = processDhcpPacketFromServer(packet);
Charles Chan3c1691a2017-02-16 14:55:22 -0800469 if (ethernetPacketAck != null) {
470 sendReply(ethernetPacketAck, dhcpPayload);
471 }
gauravf0884562016-06-17 02:47:13 +0530472 break;
473 default:
474 break;
475 }
gaurav4af95fb2016-07-07 01:48:44 +0530476 }
477
gaurav38351de2016-07-27 04:44:33 +0530478 //build the DHCP discover/request packet with gatewayip(unicast packet)
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800479 private Ethernet processDhcpPacketFromClient(PacketContext context,
480 Ethernet ethernetPacket, Set<Interface> clientInterfaces) {
481 Ip4Address relayAgentIp = getRelayAgentIPv4Address(clientInterfaces);
482 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
483 if (relayAgentIp == null || relayAgentMac == null) {
484 log.warn("Missing DHCP relay agent interface Ipv4 addr config for "
485 + "packet from client on port: {}. Aborting packet processing",
486 clientInterfaces.iterator().next().connectPoint());
Charles Chan3c1691a2017-02-16 14:55:22 -0800487 return null;
488 }
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800489 if (dhcpConnectMac == null) {
490 log.warn("DHCP {} not yet resolved .. Aborting DHCP "
491 + "packet processing from client on port: {}",
492 (dhcpGatewayIp == null) ? "server IP " + dhcpServerIp
493 : "gateway IP " + dhcpGatewayIp,
494 clientInterfaces.iterator().next().connectPoint());
495 return null;
496 }
gaurav4af95fb2016-07-07 01:48:44 +0530497 // get dhcp header.
498 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800499 etherReply.setSourceMACAddress(relayAgentMac);
500 etherReply.setDestinationMACAddress(dhcpConnectMac);
501 etherReply.setVlanID(dhcpConnectVlan.toShort());
gaurav4af95fb2016-07-07 01:48:44 +0530502 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800503 ipv4Packet.setSourceAddress(relayAgentIp.toInt());
gaurav38351de2016-07-27 04:44:33 +0530504 ipv4Packet.setDestinationAddress(dhcpServerIp.toInt());
gaurav4af95fb2016-07-07 01:48:44 +0530505 UDP udpPacket = (UDP) ipv4Packet.getPayload();
506 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800507 dhcpPacket.setGatewayIPAddress(relayAgentIp.toInt());
gaurav4af95fb2016-07-07 01:48:44 +0530508 udpPacket.setPayload(dhcpPacket);
509 ipv4Packet.setPayload(udpPacket);
510 etherReply.setPayload(ipv4Packet);
511 return etherReply;
512 }
gauravf0884562016-06-17 02:47:13 +0530513
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800514 // Returns the first v4 interface ip out of a set of interfaces or null.
515 // Checks all interfaces, and ignores v6 interface ips
516 private Ip4Address getRelayAgentIPv4Address(Set<Interface> intfs) {
517 for (Interface intf : intfs) {
518 for (InterfaceIpAddress ip : intf.ipAddressesList()) {
519 Ip4Address relayAgentIp = ip.ipAddress().getIp4Address();
520 if (relayAgentIp != null) {
521 return relayAgentIp;
522 }
523 }
Charles Chan3c1691a2017-02-16 14:55:22 -0800524 }
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800525 return null;
526 }
Charles Chan3c1691a2017-02-16 14:55:22 -0800527
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800528 //build the DHCP offer/ack with proper client port.
529 private Ethernet processDhcpPacketFromServer(Ethernet ethernetPacket) {
gaurav38351de2016-07-27 04:44:33 +0530530 // get dhcp header.
531 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
532 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
533 UDP udpPacket = (UDP) ipv4Packet.getPayload();
534 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800535
536 // determine the vlanId of the client host - note that this vlan id
537 // could be different from the vlan in the packet from the server
538 MacAddress dstMac = valueOf(dhcpPayload.getClientHardwareAddress());
539 Set<Host> hosts = hostService.getHostsByMac(dstMac);
540 if (hosts == null || hosts.isEmpty()) {
541 log.warn("Cannot determine host for DHCP client: {}. Aborting "
542 + "relay for dhcp packet from server {}",
543 dhcpPayload.getClientHardwareAddress(), ethernetPacket);
544 return null;
545 } else if (hosts.size() > 1) {
546 // XXX redo to send reply to all hosts found
547 log.warn("Multiple hosts found for mac:{}. Picking one "
548 + "host out of {}", dstMac, hosts);
549 }
550 Host host = hosts.iterator().next();
551 etherReply.setDestinationMACAddress(dstMac);
552 etherReply.setVlanID(host.vlan().toShort());
553 // we leave the srcMac from the original packet
554
555 // figure out the relay agent IP corresponding to the original request
556 Ip4Address relayAgentIP = getRelayAgentIPv4Address(
557 interfaceService.getInterfacesByPort(host.location()));
558 if (relayAgentIP == null) {
559 log.warn("Cannot determine relay agent interface Ipv4 addr for host {}. "
560 + "Aborting relay for dhcp packet from server {}",
561 host, ethernetPacket);
562 return null;
563 }
Charles Chanda5dc8c2017-01-13 14:29:29 -0800564 // SRC_IP: relay agent IP
565 // DST_IP: offered IP
566 ipv4Packet.setSourceAddress(relayAgentIP.toInt());
567 ipv4Packet.setDestinationAddress(dhcpPayload.getYourIPAddress());
568
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800569 udpPacket.setDestinationPort(UDP.DHCP_CLIENT_PORT);
gaurav38351de2016-07-27 04:44:33 +0530570 udpPacket.setPayload(dhcpPayload);
571 ipv4Packet.setPayload(udpPacket);
572 etherReply.setPayload(ipv4Packet);
573 return etherReply;
574 }
575
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800576 //send the response to the requester host.
gaurav38351de2016-07-27 04:44:33 +0530577 private void sendReply(Ethernet ethPacket, DHCP dhcpPayload) {
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800578 MacAddress descMac = valueOf(dhcpPayload.getClientHardwareAddress());
gaurav38351de2016-07-27 04:44:33 +0530579 Host host = hostService.getHost(HostId.hostId(descMac,
gaurav4af95fb2016-07-07 01:48:44 +0530580 VlanId.vlanId(ethPacket.getVlanID())));
gauravf0884562016-06-17 02:47:13 +0530581
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800582 // Send packet out to requester if the host information is available
Charles Chan3c1691a2017-02-16 14:55:22 -0800583 if (host != null) {
gauravf0884562016-06-17 02:47:13 +0530584 TrafficTreatment t = DefaultTrafficTreatment.builder()
Charles Chan3c1691a2017-02-16 14:55:22 -0800585 .setOutput(host.location().port()).build();
gauravf0884562016-06-17 02:47:13 +0530586 OutboundPacket o = new DefaultOutboundPacket(
Charles Chan3c1691a2017-02-16 14:55:22 -0800587 host.location().deviceId(), t, ByteBuffer.wrap(ethPacket.serialize()));
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800588 if (log.isTraceEnabled()) {
589 log.trace("Relaying packet to dhcp client {}", ethPacket);
590 }
gauravf0884562016-06-17 02:47:13 +0530591 packetService.emit(o);
592 }
593 }
594 }
595
596 /**
597 * Listener for network config events.
598 */
599 private class InternalConfigListener implements NetworkConfigListener {
600
601 @Override
602 public void event(NetworkConfigEvent event) {
603
604 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
605 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
606 event.configClass().equals(DhcpRelayConfig.class)) {
607 updateConfig();
608 log.info("Reconfigured");
609 }
610 }
611 }
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800612
613 /**
614 * Internal listener for host events.
615 */
616 private class InternalHostListener implements HostListener {
617 @Override
618 public void event(HostEvent event) {
619 switch (event.type()) {
620 case HOST_ADDED:
621 case HOST_UPDATED:
622 hostUpdated(event.subject());
623 break;
624 case HOST_REMOVED:
625 hostRemoved(event.subject());
626 break;
627 case HOST_MOVED:
628 // XXX todo -- moving dhcp server
629 break;
630 default:
631 break;
632 }
633 }
634 }
Lei Xu3aeaf722016-08-11 13:51:12 -0700635}