blob: 40003ebbdae6a593ddb4e999741cd5f86b6f58f9 [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;
Charles Chan23d15bc2017-05-13 00:55:21 -070036import org.onlab.packet.IpAddress;
gaurav38351de2016-07-27 04:44:33 +053037import org.onlab.packet.MacAddress;
gauravf0884562016-06-17 02:47:13 +053038import org.onlab.packet.TpPort;
39import org.onlab.packet.UDP;
gaurav4af95fb2016-07-07 01:48:44 +053040import org.onlab.packet.VlanId;
Pier Luigi20557c82017-01-12 22:40:46 -080041import org.onlab.util.Tools;
gauravf0884562016-06-17 02:47:13 +053042import org.onosproject.core.ApplicationId;
43import org.onosproject.core.CoreService;
gaurav38351de2016-07-27 04:44:33 +053044import org.onosproject.incubator.net.intf.Interface;
45import org.onosproject.incubator.net.intf.InterfaceService;
gauravf0884562016-06-17 02:47:13 +053046import org.onosproject.net.ConnectPoint;
47import org.onosproject.net.Host;
48import org.onosproject.net.HostId;
49import org.onosproject.net.config.ConfigFactory;
50import org.onosproject.net.config.NetworkConfigEvent;
51import org.onosproject.net.config.NetworkConfigListener;
52import org.onosproject.net.config.NetworkConfigRegistry;
53import org.onosproject.net.flow.DefaultTrafficSelector;
54import org.onosproject.net.flow.DefaultTrafficTreatment;
55import org.onosproject.net.flow.TrafficSelector;
56import org.onosproject.net.flow.TrafficTreatment;
Saurav Dasb0ae6ee2017-03-04 16:08:47 -080057import org.onosproject.net.host.HostEvent;
58import org.onosproject.net.host.HostListener;
gauravf0884562016-06-17 02:47:13 +053059import org.onosproject.net.host.HostService;
Saurav Dasb0ae6ee2017-03-04 16:08:47 -080060import org.onosproject.net.host.InterfaceIpAddress;
gauravf0884562016-06-17 02:47:13 +053061import org.onosproject.net.packet.DefaultOutboundPacket;
62import org.onosproject.net.packet.OutboundPacket;
63import org.onosproject.net.packet.PacketContext;
64import org.onosproject.net.packet.PacketPriority;
65import org.onosproject.net.packet.PacketProcessor;
66import org.onosproject.net.packet.PacketService;
Pier Luigi20557c82017-01-12 22:40:46 -080067import org.osgi.service.component.ComponentContext;
gauravf0884562016-06-17 02:47:13 +053068import org.slf4j.Logger;
69import org.slf4j.LoggerFactory;
70
71import com.google.common.collect.ImmutableSet;
gaurav38351de2016-07-27 04:44:33 +053072
gauravf0884562016-06-17 02:47:13 +053073import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
gaurav4af95fb2016-07-07 01:48:44 +053074import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
gaurav38351de2016-07-27 04:44:33 +053075import static org.onlab.packet.MacAddress.valueOf;
gauravf0884562016-06-17 02:47:13 +053076/**
77 * DHCP Relay Agent Application Component.
78 */
79@Component(immediate = true)
80public class DhcpRelay {
81
82 public static final String DHCP_RELAY_APP = "org.onosproject.dhcp-relay";
83 private final Logger log = LoggerFactory.getLogger(getClass());
84 private final InternalConfigListener cfgListener = new InternalConfigListener();
gaurav38351de2016-07-27 04:44:33 +053085 private static MacAddress myMAC = valueOf("4f:4f:4f:4f:4f:4f");
gauravf0884562016-06-17 02:47:13 +053086
87 private final Set<ConfigFactory> factories = ImmutableSet.of(
88 new ConfigFactory<ApplicationId, DhcpRelayConfig>(APP_SUBJECT_FACTORY,
89 DhcpRelayConfig.class,
90 "dhcprelay") {
91 @Override
92 public DhcpRelayConfig createConfig() {
93 return new DhcpRelayConfig();
94 }
95 }
96 );
97
98 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
99 protected NetworkConfigRegistry cfgService;
100
101 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
102 protected CoreService coreService;
103
104 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
105 protected PacketService packetService;
106
107 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
108 protected HostService hostService;
109
gaurav38351de2016-07-27 04:44:33 +0530110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected InterfaceService interfaceService;
112
Pier Luigi20557c82017-01-12 22:40:46 -0800113 @Property(name = "arpEnabled", boolValue = true,
114 label = "Enable Address resolution protocol")
115 protected boolean arpEnabled = true;
116
gauravf0884562016-06-17 02:47:13 +0530117 private DhcpRelayPacketProcessor dhcpRelayPacketProcessor = new DhcpRelayPacketProcessor();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800118 private InternalHostListener hostListener = new InternalHostListener();
119
gaurav38351de2016-07-27 04:44:33 +0530120 private Ip4Address dhcpServerIp = null;
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800121 // dhcp server may be connected directly to the SDN network or
122 // via an external gateway. When connected directly, the dhcpConnectPoint, dhcpConnectMac,
123 // and dhcpConnectVlan refer to the server. When connected via the gateway, they refer
124 // to the gateway.
125 private ConnectPoint dhcpServerConnectPoint = null;
126 private MacAddress dhcpConnectMac = null;
127 private VlanId dhcpConnectVlan = null;
128 private Ip4Address dhcpGatewayIp = null;
gauravf0884562016-06-17 02:47:13 +0530129 private ApplicationId appId;
130
131 @Activate
Pier Luigi20557c82017-01-12 22:40:46 -0800132 protected void activate(ComponentContext context) {
gauravf0884562016-06-17 02:47:13 +0530133 //start the dhcp relay agent
gauravf0884562016-06-17 02:47:13 +0530134 appId = coreService.registerApplication(DHCP_RELAY_APP);
135
136 cfgService.addListener(cfgListener);
137 factories.forEach(cfgService::registerConfigFactory);
138 //update the dhcp server configuration.
139 updateConfig();
140 //add the packet services.
141 packetService.addProcessor(dhcpRelayPacketProcessor, PacketProcessor.director(0));
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800142 hostService.addListener(hostListener);
Pier Luigi20557c82017-01-12 22:40:46 -0800143 requestDhcpPackets();
144 modified(context);
145
gauravf0884562016-06-17 02:47:13 +0530146 log.info("DHCP-RELAY Started");
gauravf0884562016-06-17 02:47:13 +0530147 }
148
149 @Deactivate
150 protected void deactivate() {
151 cfgService.removeListener(cfgListener);
152 factories.forEach(cfgService::unregisterConfigFactory);
153 packetService.removeProcessor(dhcpRelayPacketProcessor);
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800154 hostService.removeListener(hostListener);
Pier Luigi20557c82017-01-12 22:40:46 -0800155 cancelDhcpPackets();
156 cancelArpPackets();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800157 if (dhcpGatewayIp != null) {
158 hostService.stopMonitoringIp(dhcpGatewayIp);
159 } else {
160 hostService.stopMonitoringIp(dhcpServerIp);
161 }
Pier Luigi20557c82017-01-12 22:40:46 -0800162
gauravf0884562016-06-17 02:47:13 +0530163 log.info("DHCP-RELAY Stopped");
164 }
165
Pier Luigi20557c82017-01-12 22:40:46 -0800166 @Modified
167 protected void modified(ComponentContext context) {
168 Dictionary<?, ?> properties = context.getProperties();
169 Boolean flag;
170
171 flag = Tools.isPropertyEnabled(properties, "arpEnabled");
172 if (flag != null) {
173 arpEnabled = flag;
174 log.info("Address resolution protocol is {}",
175 arpEnabled ? "enabled" : "disabled");
176 }
177
178 if (arpEnabled) {
179 requestArpPackets();
180 } else {
181 cancelArpPackets();
182 }
183 }
184
Charles Chan3c1691a2017-02-16 14:55:22 -0800185 /**
186 * Checks if this app has been configured.
187 *
188 * @return true if all information we need have been initialized
189 */
190 private boolean configured() {
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800191 return dhcpServerConnectPoint != null && dhcpServerIp != null;
Charles Chan3c1691a2017-02-16 14:55:22 -0800192 }
193
gauravf0884562016-06-17 02:47:13 +0530194 private void updateConfig() {
195 DhcpRelayConfig cfg = cfgService.getConfig(appId, DhcpRelayConfig.class);
gauravf0884562016-06-17 02:47:13 +0530196 if (cfg == null) {
197 log.warn("Dhcp Server info not available");
198 return;
199 }
200
201 dhcpServerConnectPoint = cfg.getDhcpServerConnectPoint();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800202 Ip4Address oldDhcpServerIp = dhcpServerIp;
203 Ip4Address oldDhcpGatewayIp = dhcpGatewayIp;
gaurav38351de2016-07-27 04:44:33 +0530204 dhcpServerIp = cfg.getDhcpServerIp();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800205 dhcpGatewayIp = cfg.getDhcpGatewayIp();
206 dhcpConnectMac = null; // reset for updated config
207 dhcpConnectVlan = null; // reset for updated config
Charles Chan23d15bc2017-05-13 00:55:21 -0700208 log.info("DHCP server connect point: " + dhcpServerConnectPoint);
209 log.info("DHCP server ipaddress " + dhcpServerIp);
210
211 IpAddress ipToProbe = dhcpGatewayIp != null ? dhcpGatewayIp : dhcpServerIp;
212 String hostToProbe = dhcpGatewayIp != null ? "gateway" : "DHCP server";
213
214 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
215 if (hosts.isEmpty()) {
216 log.info("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
217 if (oldDhcpGatewayIp != null) {
218 hostService.stopMonitoringIp(oldDhcpGatewayIp);
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800219 }
Charles Chan23d15bc2017-05-13 00:55:21 -0700220 if (oldDhcpServerIp != null) {
221 hostService.stopMonitoringIp(oldDhcpServerIp);
222 }
223 hostService.startMonitoringIp(ipToProbe);
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800224 } else {
Charles Chan23d15bc2017-05-13 00:55:21 -0700225 // Probe target is known; There should be only 1 host with this ip
226 hostUpdated(hosts.iterator().next());
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800227 }
228 }
229
230 private void hostRemoved(Host host) {
231 if (host.ipAddresses().contains(dhcpServerIp)) {
232 log.warn("DHCP server {} removed", dhcpServerIp);
233 dhcpConnectMac = null;
234 dhcpConnectVlan = null;
235 }
236 if (dhcpGatewayIp != null && host.ipAddresses().contains(dhcpGatewayIp)) {
237 log.warn("DHCP gateway {} removed", dhcpGatewayIp);
238 dhcpConnectMac = null;
239 dhcpConnectVlan = null;
240 }
241 }
242
243 private void hostUpdated(Host host) {
244 if (dhcpGatewayIp != null) {
245 if (host.ipAddresses().contains(dhcpGatewayIp)) {
246 dhcpConnectMac = host.mac();
247 dhcpConnectVlan = host.vlan();
248 log.info("DHCP gateway {} resolved to Mac/Vlan:{}/{}", dhcpGatewayIp,
249 dhcpConnectMac, dhcpConnectVlan);
250 }
251 return;
252 }
253 if (host.ipAddresses().contains(dhcpServerIp)) {
254 dhcpConnectMac = host.mac();
255 dhcpConnectVlan = host.vlan();
256 log.info("DHCP server {} resolved to Mac/Vlan:{}/{}", dhcpServerIp,
257 dhcpConnectMac, dhcpConnectVlan);
258 }
gauravf0884562016-06-17 02:47:13 +0530259 }
260
261 /**
Pier Luigi20557c82017-01-12 22:40:46 -0800262 * Request DHCP packet in via PacketService.
gauravf0884562016-06-17 02:47:13 +0530263 */
Pier Luigi20557c82017-01-12 22:40:46 -0800264 private void requestDhcpPackets() {
gauravf0884562016-06-17 02:47:13 +0530265 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
266 .matchEthType(Ethernet.TYPE_IPV4)
267 .matchIPProtocol(IPv4.PROTOCOL_UDP)
gaurav38351de2016-07-27 04:44:33 +0530268 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
gauravf0884562016-06-17 02:47:13 +0530269 packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
270
271 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
272 .matchEthType(Ethernet.TYPE_IPV4)
273 .matchIPProtocol(IPv4.PROTOCOL_UDP)
gaurav38351de2016-07-27 04:44:33 +0530274 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
gauravf0884562016-06-17 02:47:13 +0530275 packetService.requestPackets(selectorClient.build(), PacketPriority.CONTROL, appId);
276 }
277
278 /**
Pier Luigi20557c82017-01-12 22:40:46 -0800279 * Cancel requested DHCP packets in via packet service.
gauravf0884562016-06-17 02:47:13 +0530280 */
Pier Luigi20557c82017-01-12 22:40:46 -0800281 private void cancelDhcpPackets() {
gauravf0884562016-06-17 02:47:13 +0530282 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
283 .matchEthType(Ethernet.TYPE_IPV4)
284 .matchIPProtocol(IPv4.PROTOCOL_UDP)
gaurav38351de2016-07-27 04:44:33 +0530285 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
gauravf0884562016-06-17 02:47:13 +0530286 packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
287
288 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
289 .matchEthType(Ethernet.TYPE_IPV4)
290 .matchIPProtocol(IPv4.PROTOCOL_UDP)
gaurav38351de2016-07-27 04:44:33 +0530291 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
gauravf0884562016-06-17 02:47:13 +0530292 packetService.cancelPackets(selectorClient.build(), PacketPriority.CONTROL, appId);
Pier Luigi20557c82017-01-12 22:40:46 -0800293 }
gaurav38351de2016-07-27 04:44:33 +0530294
Pier Luigi20557c82017-01-12 22:40:46 -0800295 /**
296 * Request ARP packet in via PacketService.
297 */
298 private void requestArpPackets() {
299 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
300 .matchEthType(Ethernet.TYPE_ARP);
301 packetService.requestPackets(selectorArpServer.build(), PacketPriority.CONTROL, appId);
302 }
303
304 /**
305 * Cancel requested ARP packets in via packet service.
306 */
307 private void cancelArpPackets() {
gaurav38351de2016-07-27 04:44:33 +0530308 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
309 .matchEthType(Ethernet.TYPE_ARP);
310 packetService.cancelPackets(selectorArpServer.build(), PacketPriority.CONTROL, appId);
gauravf0884562016-06-17 02:47:13 +0530311 }
312
313 private class DhcpRelayPacketProcessor implements PacketProcessor {
314
315 @Override
316 public void process(PacketContext context) {
Charles Chan3c1691a2017-02-16 14:55:22 -0800317 if (!configured()) {
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800318 log.warn("Missing DHCP relay server config. Abort packet processing");
Charles Chan3c1691a2017-02-16 14:55:22 -0800319 return;
320 }
321
gauravf0884562016-06-17 02:47:13 +0530322 // process the packet and get the payload
323 Ethernet packet = context.inPacket().parsed();
gauravf0884562016-06-17 02:47:13 +0530324 if (packet == null) {
325 return;
326 }
327
328 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
329 IPv4 ipv4Packet = (IPv4) packet.getPayload();
330
331 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
332 UDP udpPacket = (UDP) ipv4Packet.getPayload();
333 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
gaurav38351de2016-07-27 04:44:33 +0530334 if (udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT ||
335 udpPacket.getSourcePort() == UDP.DHCP_SERVER_PORT) {
gaurav4af95fb2016-07-07 01:48:44 +0530336 //This packet is dhcp.
337 processDhcpPacket(context, dhcpPayload);
gauravf0884562016-06-17 02:47:13 +0530338 }
339 }
Pier Luigi20557c82017-01-12 22:40:46 -0800340 } else if (packet.getEtherType() == Ethernet.TYPE_ARP && arpEnabled) {
gaurav38351de2016-07-27 04:44:33 +0530341 ARP arpPacket = (ARP) packet.getPayload();
342 Set<Interface> serverInterfaces = interfaceService.
343 getInterfacesByPort(context.inPacket().receivedFrom());
344 //ignore the packets if dhcp server interface is not configured on onos.
345 if (serverInterfaces.isEmpty()) {
346 log.warn("server virtual interface not configured");
347 return;
348 }
349 if ((arpPacket.getOpCode() == ARP.OP_REQUEST) &&
350 checkArpRequestFrmDhcpServ(serverInterfaces, arpPacket)) {
351 processArpPacket(context, packet);
352 }
gauravf0884562016-06-17 02:47:13 +0530353 }
354 }
355
gaurav38351de2016-07-27 04:44:33 +0530356 //method to check the arp request is from dhcp server for default-gateway.
357 private boolean checkArpRequestFrmDhcpServ(Set<Interface> serverInterfaces, ARP arpPacket) {
358 if (Objects.equals(serverInterfaces.iterator().next().ipAddressesList().get(0).
359 ipAddress().getIp4Address(),
360 Ip4Address.valueOf(arpPacket.getTargetProtocolAddress()))) {
361 return true;
362 }
363 return false;
364 }
365
gauravf0884562016-06-17 02:47:13 +0530366 //forward the packet to ConnectPoint where the DHCP server is attached.
gaurav4af95fb2016-07-07 01:48:44 +0530367 private void forwardPacket(Ethernet packet) {
gauravf0884562016-06-17 02:47:13 +0530368 //send Packetout to dhcp server connectpoint.
369 if (dhcpServerConnectPoint != null) {
370 TrafficTreatment t = DefaultTrafficTreatment.builder()
371 .setOutput(dhcpServerConnectPoint.port()).build();
372 OutboundPacket o = new DefaultOutboundPacket(
gaurav4af95fb2016-07-07 01:48:44 +0530373 dhcpServerConnectPoint.deviceId(), t, ByteBuffer.wrap(packet.serialize()));
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800374 if (log.isTraceEnabled()) {
375 log.trace("Relaying packet to dhcp server {}", packet);
376 }
gauravf0884562016-06-17 02:47:13 +0530377 packetService.emit(o);
378 }
379 }
380
gaurav38351de2016-07-27 04:44:33 +0530381 /**
382 * Processes the ARP Payload and initiates a reply to the client.
383 *
384 * @param context context of the incoming message
385 * @param packet the ethernet payload
386 */
387 private void processArpPacket(PacketContext context, Ethernet packet) {
gaurav38351de2016-07-27 04:44:33 +0530388 ARP arpPacket = (ARP) packet.getPayload();
389
390 ARP arpReply = (ARP) arpPacket.clone();
391 arpReply.setOpCode(ARP.OP_REPLY);
392
393 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
394 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
395 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
396 arpReply.setSenderHardwareAddress(myMAC.toBytes());
397
398 // Ethernet Frame.
399 Ethernet ethReply = new Ethernet();
400 ethReply.setSourceMACAddress(myMAC);
401 ethReply.setDestinationMACAddress(packet.getSourceMAC());
402 ethReply.setEtherType(Ethernet.TYPE_ARP);
403 ethReply.setVlanID(packet.getVlanID());
404
405 ethReply.setPayload(arpReply);
406 forwardPacket(ethReply);
407 }
408
gaurav4af95fb2016-07-07 01:48:44 +0530409 //process the dhcp packet before sending to server
gauravf0884562016-06-17 02:47:13 +0530410 private void processDhcpPacket(PacketContext context, DHCP dhcpPayload) {
Charles Chan1b201232016-12-07 15:39:22 -0800411 ConnectPoint inPort = context.inPacket().receivedFrom();
412 Set<Interface> clientServerInterfaces = interfaceService.getInterfacesByPort(inPort);
gaurav38351de2016-07-27 04:44:33 +0530413 //ignore the packets if dhcp client interface is not configured on onos.
414 if (clientServerInterfaces.isEmpty()) {
Charles Chan1b201232016-12-07 15:39:22 -0800415 log.warn("Virtual interface is not configured on {}", inPort);
gaurav38351de2016-07-27 04:44:33 +0530416 return;
417 }
418
gauravf0884562016-06-17 02:47:13 +0530419 if (dhcpPayload == null) {
420 return;
421 }
gaurav4af95fb2016-07-07 01:48:44 +0530422
gauravf0884562016-06-17 02:47:13 +0530423 Ethernet packet = context.inPacket().parsed();
424 DHCPPacketType incomingPacketType = null;
425 for (DHCPOption option : dhcpPayload.getOptions()) {
426 if (option.getCode() == OptionCode_MessageType.getValue()) {
427 byte[] data = option.getData();
428 incomingPacketType = DHCPPacketType.getType(data[0]);
429 }
430 }
gaurav38351de2016-07-27 04:44:33 +0530431
gauravf0884562016-06-17 02:47:13 +0530432 switch (incomingPacketType) {
433 case DHCPDISCOVER:
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800434 // add the gatewayip as virtual interface ip for server to understand
435 // the lease to be assigned and forward the packet to dhcp server.
436 Ethernet ethernetPacketDiscover =
437 processDhcpPacketFromClient(context, packet, clientServerInterfaces);
Charles Chan3c1691a2017-02-16 14:55:22 -0800438 if (ethernetPacketDiscover != null) {
439 forwardPacket(ethernetPacketDiscover);
440 }
gaurav4af95fb2016-07-07 01:48:44 +0530441 break;
442 case DHCPOFFER:
443 //reply to dhcp client.
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800444 Ethernet ethernetPacketOffer = processDhcpPacketFromServer(packet);
Charles Chan3c1691a2017-02-16 14:55:22 -0800445 if (ethernetPacketOffer != null) {
446 sendReply(ethernetPacketOffer, dhcpPayload);
447 }
gaurav4af95fb2016-07-07 01:48:44 +0530448 break;
449 case DHCPREQUEST:
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800450 // add the gatewayip as virtual interface ip for server to understand
451 // the lease to be assigned and forward the packet to dhcp server.
452 Ethernet ethernetPacketRequest =
453 processDhcpPacketFromClient(context, packet, clientServerInterfaces);
Charles Chan3c1691a2017-02-16 14:55:22 -0800454 if (ethernetPacketRequest != null) {
455 forwardPacket(ethernetPacketRequest);
456 }
gaurav4af95fb2016-07-07 01:48:44 +0530457 break;
458 case DHCPACK:
459 //reply to dhcp client.
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800460 Ethernet ethernetPacketAck = processDhcpPacketFromServer(packet);
Charles Chan3c1691a2017-02-16 14:55:22 -0800461 if (ethernetPacketAck != null) {
462 sendReply(ethernetPacketAck, dhcpPayload);
463 }
gauravf0884562016-06-17 02:47:13 +0530464 break;
465 default:
466 break;
467 }
gaurav4af95fb2016-07-07 01:48:44 +0530468 }
469
gaurav38351de2016-07-27 04:44:33 +0530470 //build the DHCP discover/request packet with gatewayip(unicast packet)
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800471 private Ethernet processDhcpPacketFromClient(PacketContext context,
472 Ethernet ethernetPacket, Set<Interface> clientInterfaces) {
473 Ip4Address relayAgentIp = getRelayAgentIPv4Address(clientInterfaces);
474 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
475 if (relayAgentIp == null || relayAgentMac == null) {
476 log.warn("Missing DHCP relay agent interface Ipv4 addr config for "
477 + "packet from client on port: {}. Aborting packet processing",
478 clientInterfaces.iterator().next().connectPoint());
Charles Chan3c1691a2017-02-16 14:55:22 -0800479 return null;
480 }
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800481 if (dhcpConnectMac == null) {
482 log.warn("DHCP {} not yet resolved .. Aborting DHCP "
483 + "packet processing from client on port: {}",
484 (dhcpGatewayIp == null) ? "server IP " + dhcpServerIp
485 : "gateway IP " + dhcpGatewayIp,
486 clientInterfaces.iterator().next().connectPoint());
487 return null;
488 }
gaurav4af95fb2016-07-07 01:48:44 +0530489 // get dhcp header.
490 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800491 etherReply.setSourceMACAddress(relayAgentMac);
492 etherReply.setDestinationMACAddress(dhcpConnectMac);
493 etherReply.setVlanID(dhcpConnectVlan.toShort());
gaurav4af95fb2016-07-07 01:48:44 +0530494 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800495 ipv4Packet.setSourceAddress(relayAgentIp.toInt());
gaurav38351de2016-07-27 04:44:33 +0530496 ipv4Packet.setDestinationAddress(dhcpServerIp.toInt());
gaurav4af95fb2016-07-07 01:48:44 +0530497 UDP udpPacket = (UDP) ipv4Packet.getPayload();
498 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800499 dhcpPacket.setGatewayIPAddress(relayAgentIp.toInt());
gaurav4af95fb2016-07-07 01:48:44 +0530500 udpPacket.setPayload(dhcpPacket);
501 ipv4Packet.setPayload(udpPacket);
502 etherReply.setPayload(ipv4Packet);
503 return etherReply;
504 }
gauravf0884562016-06-17 02:47:13 +0530505
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800506 // Returns the first v4 interface ip out of a set of interfaces or null.
507 // Checks all interfaces, and ignores v6 interface ips
508 private Ip4Address getRelayAgentIPv4Address(Set<Interface> intfs) {
509 for (Interface intf : intfs) {
510 for (InterfaceIpAddress ip : intf.ipAddressesList()) {
511 Ip4Address relayAgentIp = ip.ipAddress().getIp4Address();
512 if (relayAgentIp != null) {
513 return relayAgentIp;
514 }
515 }
Charles Chan3c1691a2017-02-16 14:55:22 -0800516 }
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800517 return null;
518 }
Charles Chan3c1691a2017-02-16 14:55:22 -0800519
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800520 //build the DHCP offer/ack with proper client port.
521 private Ethernet processDhcpPacketFromServer(Ethernet ethernetPacket) {
gaurav38351de2016-07-27 04:44:33 +0530522 // get dhcp header.
523 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
524 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
525 UDP udpPacket = (UDP) ipv4Packet.getPayload();
526 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800527
528 // determine the vlanId of the client host - note that this vlan id
529 // could be different from the vlan in the packet from the server
530 MacAddress dstMac = valueOf(dhcpPayload.getClientHardwareAddress());
531 Set<Host> hosts = hostService.getHostsByMac(dstMac);
532 if (hosts == null || hosts.isEmpty()) {
533 log.warn("Cannot determine host for DHCP client: {}. Aborting "
534 + "relay for dhcp packet from server {}",
535 dhcpPayload.getClientHardwareAddress(), ethernetPacket);
536 return null;
537 } else if (hosts.size() > 1) {
538 // XXX redo to send reply to all hosts found
539 log.warn("Multiple hosts found for mac:{}. Picking one "
540 + "host out of {}", dstMac, hosts);
541 }
542 Host host = hosts.iterator().next();
543 etherReply.setDestinationMACAddress(dstMac);
544 etherReply.setVlanID(host.vlan().toShort());
545 // we leave the srcMac from the original packet
546
547 // figure out the relay agent IP corresponding to the original request
548 Ip4Address relayAgentIP = getRelayAgentIPv4Address(
549 interfaceService.getInterfacesByPort(host.location()));
550 if (relayAgentIP == null) {
551 log.warn("Cannot determine relay agent interface Ipv4 addr for host {}. "
552 + "Aborting relay for dhcp packet from server {}",
553 host, ethernetPacket);
554 return null;
555 }
Charles Chanda5dc8c2017-01-13 14:29:29 -0800556 // SRC_IP: relay agent IP
557 // DST_IP: offered IP
558 ipv4Packet.setSourceAddress(relayAgentIP.toInt());
559 ipv4Packet.setDestinationAddress(dhcpPayload.getYourIPAddress());
560
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800561 udpPacket.setDestinationPort(UDP.DHCP_CLIENT_PORT);
gaurav38351de2016-07-27 04:44:33 +0530562 udpPacket.setPayload(dhcpPayload);
563 ipv4Packet.setPayload(udpPacket);
564 etherReply.setPayload(ipv4Packet);
565 return etherReply;
566 }
567
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800568 //send the response to the requester host.
gaurav38351de2016-07-27 04:44:33 +0530569 private void sendReply(Ethernet ethPacket, DHCP dhcpPayload) {
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800570 MacAddress descMac = valueOf(dhcpPayload.getClientHardwareAddress());
gaurav38351de2016-07-27 04:44:33 +0530571 Host host = hostService.getHost(HostId.hostId(descMac,
gaurav4af95fb2016-07-07 01:48:44 +0530572 VlanId.vlanId(ethPacket.getVlanID())));
gauravf0884562016-06-17 02:47:13 +0530573
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800574 // Send packet out to requester if the host information is available
Charles Chan3c1691a2017-02-16 14:55:22 -0800575 if (host != null) {
gauravf0884562016-06-17 02:47:13 +0530576 TrafficTreatment t = DefaultTrafficTreatment.builder()
Charles Chan3c1691a2017-02-16 14:55:22 -0800577 .setOutput(host.location().port()).build();
gauravf0884562016-06-17 02:47:13 +0530578 OutboundPacket o = new DefaultOutboundPacket(
Charles Chan3c1691a2017-02-16 14:55:22 -0800579 host.location().deviceId(), t, ByteBuffer.wrap(ethPacket.serialize()));
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800580 if (log.isTraceEnabled()) {
581 log.trace("Relaying packet to dhcp client {}", ethPacket);
582 }
gauravf0884562016-06-17 02:47:13 +0530583 packetService.emit(o);
584 }
585 }
586 }
587
588 /**
589 * Listener for network config events.
590 */
591 private class InternalConfigListener implements NetworkConfigListener {
592
593 @Override
594 public void event(NetworkConfigEvent event) {
595
596 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
597 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
598 event.configClass().equals(DhcpRelayConfig.class)) {
599 updateConfig();
600 log.info("Reconfigured");
601 }
602 }
603 }
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800604
605 /**
606 * Internal listener for host events.
607 */
608 private class InternalHostListener implements HostListener {
609 @Override
610 public void event(HostEvent event) {
611 switch (event.type()) {
612 case HOST_ADDED:
613 case HOST_UPDATED:
614 hostUpdated(event.subject());
615 break;
616 case HOST_REMOVED:
617 hostRemoved(event.subject());
618 break;
619 case HOST_MOVED:
620 // XXX todo -- moving dhcp server
621 break;
622 default:
623 break;
624 }
625 }
626 }
Lei Xu3aeaf722016-08-11 13:51:12 -0700627}