2 * Copyright (c) 2014 SDN Hub. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
11 package org.sdnhub.dnsguard;
13 import java.io.BufferedReader;
15 import java.io.FileInputStream;
16 import java.io.FileNotFoundException;
17 import java.io.FileOutputStream;
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.io.InputStreamReader;
21 import java.io.OutputStream;
22 import java.io.StringWriter;
24 import java.util.ArrayList;
25 import java.util.Enumeration;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Properties;
31 import net.dougharris.dns.RFC1035;
33 import org.opendaylight.controller.hosttracker.IfHostListener;
34 import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector;
35 import org.opendaylight.controller.sal.action.Action;
36 import org.opendaylight.controller.sal.action.Controller;
37 import org.opendaylight.controller.sal.core.Node;
38 import org.opendaylight.controller.sal.core.NodeConnector;
39 import org.opendaylight.controller.sal.core.Property;
40 import org.opendaylight.controller.sal.core.UpdateType;
41 import org.opendaylight.controller.sal.flowprogrammer.Flow;
42 import org.opendaylight.controller.sal.flowprogrammer.IFlowProgrammerService;
43 import org.opendaylight.controller.sal.inventory.IListenInventoryUpdates;
44 import org.opendaylight.controller.sal.match.Match;
45 import org.opendaylight.controller.sal.match.MatchField;
46 import org.opendaylight.controller.sal.match.MatchType;
47 import org.opendaylight.controller.sal.packet.Ethernet;
48 import org.opendaylight.controller.sal.packet.IDataPacketService;
49 import org.opendaylight.controller.sal.packet.IListenDataPacket;
50 import org.opendaylight.controller.sal.packet.IPv4;
51 import org.opendaylight.controller.sal.packet.Packet;
52 import org.opendaylight.controller.sal.packet.PacketResult;
53 import org.opendaylight.controller.sal.packet.RawPacket;
54 import org.opendaylight.controller.sal.packet.UDP;
55 import org.opendaylight.controller.sal.utils.EtherTypes;
56 import org.opendaylight.controller.sal.utils.NetUtils;
57 import org.opendaylight.controller.sal.utils.ServiceHelper;
58 import org.opendaylight.controller.sal.utils.Status;
59 import org.opendaylight.controller.switchmanager.ISwitchManager;
60 import org.osgi.framework.BundleContext;
61 import org.osgi.framework.FrameworkUtil;
62 import org.sdnhub.dnsguard.renders.DnsRecordReply;
63 import org.sdnhub.dnsguard.renders.DnsUsage;
64 import org.sdnhub.dnsguard.renders.Violator;
65 //import org.sdnhub.learningswitch.ILearningSwitch;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
69 import org.hsqldb.Server;
71 public class DnsGuard implements IListenDataPacket, IListenInventoryUpdates,
72 IfHostListener, IDnsGuard {
74 private Server hsqlServer;
75 private DnsGuardPersistence database;
77 protected static final Logger log = LoggerFactory.getLogger(DnsGuard.class);
78 private String dbconfig_filename = "database.config";
80 private IDataPacketService dataPacketService = null;
82 void setDataPacketService(IDataPacketService s) {
83 this.dataPacketService = s;
86 void unsetDataPacketService(IDataPacketService s) {
87 if (this.dataPacketService == s) {
88 this.dataPacketService = null;
92 private ISwitchManager swManager = null;
94 void setSwitchManager(ISwitchManager s) {
95 log.info("set ISwitchManager");
99 void unsetSwitchManager(ISwitchManager s) {
100 if (this.swManager == s) {
101 this.swManager = null;
105 private IFlowProgrammerService programmer = null;
107 public void setFlowProgrammerService(IFlowProgrammerService s) {
111 public void unsetFlowProgrammerService(IFlowProgrammerService s) {
112 if (this.programmer == s) {
113 this.programmer = null;
118 public PacketResult receiveDataPacket(RawPacket inPkt) {
120 // log.info("packet arrived");
122 Packet etherpack = this.dataPacketService.decodeDataPacket(inPkt);
124 if (etherpack instanceof Ethernet) {
126 Packet ippack = etherpack.getPayload();
128 if (ippack instanceof IPv4) {
130 Packet udppackt = ippack.getPayload();
132 if (udppackt instanceof UDP) {
135 short sport = ((UDP) udppackt).getSourcePort();
136 short dport = ((UDP) udppackt).getDestinationPort();
138 if (sport == 53 || dport == 53) {
140 int sip = ((IPv4) ippack).getSourceAddress();
141 int dip = ((IPv4) ippack).getDestinationAddress();
143 byte[] dnspacket = ((UDP) udppackt).getRawPayload();
145 if (dnspacket != null) {
147 RFC1035 dns_msg = new RFC1035(dnspacket);
152 if (dns_msg.qr == false) {
155 "DNS REPLY len: {} from: {} to: {} dport: {}",
156 dnspacket.length, NetUtils
158 .toString(), NetUtils
162 DnsReply reply = new DnsReply(dns_msg);
164 if (dns_msg.getCountAnswer() > 0) {
165 reply.setPacketHeaders(NetUtils
166 .getInetAddress(sip).toString()
167 .replace("/", ""), NetUtils
168 .getInetAddress(dip).toString()
169 .replace("/", ""), sport, dport);
170 database.save(reply);
174 } catch (Exception ex) {
176 log.error(ex.getLocalizedMessage());
184 return PacketResult.KEEP_PROCESSING;
189 // ILearningSwitch simple = (ILearningSwitch) ServiceHelper.getInstance(
190 // ILearningSwitch.class, "default", this);
191 // if (simple == null) {
192 // log.error("Not able to find the learningswitch");
194 // log.info("learningswitch found, loaded before dnsguard OK");
197 // read info from config file
203 // hsqlServer = new Server();
205 // // HSQLDB prints out a lot of informations when
206 // // starting and closing, which we don't need now.
207 // // Normally you should point the setLogWriter
208 // // to some Writer object that could store the logs.
209 // hsqlServer.setLogWriter(null);
210 // hsqlServer.setSilent(true);
212 // // The actual database will be named 'xdb' and its
213 // // settings and data will be stored in files
214 // // testdb.properties and testdb.script
215 // hsqlServer.setDatabaseName(0, "dnsspy");
216 // hsqlServer.setDatabasePath(0, "file:dbfile");
218 // // Start the database!
219 // hsqlServer.start();
225 if (hsqlServer != null) {
230 private String getDatabaseSql(){
232 BundleContext bundleContext = FrameworkUtil.getBundle( this.getClass()).getBundleContext();
234 URL urlfile = bundleContext.getBundle().getResource("/database.sql");
239 String sqlCreation = "";
243 input = urlfile.openConnection().getInputStream();
245 BufferedReader br = new BufferedReader(new InputStreamReader(input));
248 while ((line = br.readLine()) != null) {
249 sqlCreation = sqlCreation + line;
252 } catch (IOException e) {
253 // TODO Auto-generated catch block
260 private void connectToDb(){
262 String sqlCreation = getDatabaseSql();
263 Properties prop = getStartupConfig(dbconfig_filename);
266 database = new DnsGuardPersistence(prop.getProperty("dbserver"),
267 Integer.valueOf(prop.getProperty("dbport")),
268 prop.getProperty("dbname"), prop.getProperty("dbuser"),
269 prop.getProperty("dbpasswd"), Integer.valueOf(prop
270 .getProperty("internal_buffer_init_size")),
271 Integer.valueOf(prop.getProperty("internal_buffer_max_size")), sqlCreation);
274 boolean dbconx = database.Connect();
276 log.info("Database connected: {} ", dbconx);
280 * This method read from a file shipped with the bundle...
281 * This method is deprecated, and still in code to have a reference how to read resources
283 // public Properties getStartupConfig(String filename) {
285 // // http://www.mkyong.com/java/java-properties-file-examples/
287 // Properties prop = new Properties();
288 // InputStream input = null;
292 // BundleContext bundleContext = FrameworkUtil.getBundle(
293 // this.getClass()).getBundleContext();
295 // URL urlfile = bundleContext.getBundle().getResource(
296 // "/database.config");
298 // // input = new FileInputStream(
299 // // bundleContext.getBundle().getResource("database.config").to );
301 // input = urlfile.openConnection().getInputStream();
303 // // load a properties file
308 // Enumeration<?> e = prop.propertyNames();
309 // while (e.hasMoreElements()) {
310 // String key = (String) e.nextElement();
311 // String value = prop.getProperty(key);
312 // log.info(key + ": " + value);
315 // //LC: This section is for using a file in the local bundle storage (seems only available at running time)
316 // File foutput = bundleContext.getDataFile("dns-guard-save.txt");
317 // OutputStream fostream = new FileOutputStream(foutput);
319 // Properties newprop = new Properties();
320 // newprop.put("newval", "luis");
322 // newprop.store(fostream, "");
328 // } catch (IOException ex) {
329 // ex.printStackTrace();
331 // if (input != null) {
334 // } catch (IOException e) {
335 // e.printStackTrace();
344 public Properties getStartupConfig(String filename) {
346 // http://www.mkyong.com/java/java-properties-file-examples/
348 Properties prop = new Properties();
349 InputStream input = null;
353 BundleContext bundleContext = FrameworkUtil.getBundle(
354 this.getClass()).getBundleContext();
356 File configfile = bundleContext.getDataFile( dbconfig_filename );
358 input = new FileInputStream(configfile);
360 // load a properties file
365 Enumeration<?> e = prop.propertyNames();
366 while (e.hasMoreElements()) {
367 String key = (String) e.nextElement();
368 String value = prop.getProperty(key);
369 log.info(key + ": " + value);
374 } catch (IOException ex) {
375 ex.printStackTrace();
380 } catch (IOException e) {
391 public String echo(String in) {
392 return in + " from class";
396 public List<String> lazyresolv(String appIp) {
398 // select distinct request FROM dnsspy.bulkreply where data =
400 if (database.Connect()) {
401 return database.lazyresolv(appIp);
404 return new ArrayList<String>();
408 public List<String> appsbyip(String sourceIp) {
410 // select distinct request FROM dnsspy.bulkreply where data =
412 if (database.Connect()) {
413 return database.appsbyip(sourceIp);
416 return new ArrayList<String>();
420 public void updateNode(Node node, UpdateType type, Set<Property> props) {
421 // log.info("updateNode, NodeType {} Update {} Prop {}",
422 // node.getID().toString(), type.toString(), props.toString());
424 if (type == UpdateType.ADDED) {
426 for (Iterator<Property> it = props.iterator(); it.hasNext();) {
428 Property pval = it.next();
430 // log.info("updateNode {} - {}", pval.getName().toString(),
431 // pval.getStringValue());
433 if (pval.getName() == "macAddress") {
435 log.info("sw is up");
437 // forward dns traffic to controller: TP_DST, (short)53
438 Match match = new Match();
439 match.setField(new MatchField(MatchType.DL_TYPE,
440 EtherTypes.IPv4.shortValue()));
441 match.setField(new MatchField(MatchType.NW_PROTO, (byte) 17));
442 match.setField(new MatchField(MatchType.TP_DST, (short) 53));
444 List<Action> actions = new ArrayList<Action>();
445 actions.add(new Controller());
447 Flow flow = new Flow(match, actions);
448 flow.setIdleTimeout((short) 0);
449 flow.setHardTimeout((short) 0);
450 flow.setPriority((short) 32769);
452 // Modify the flow on the network node
453 Status status = programmer.addFlow(node, flow);
455 if (!status.isSuccess()) {
457 "SDN Plugin failed to program the flow: {}. The failure is: {}",
458 flow, status.getDescription());
461 // forward dns traffic to controller: TP_SRC, (short)53
462 // match = new Match();
463 // match.setField( new MatchField(MatchType.DL_TYPE,
464 // EtherTypes.IPv4.shortValue()));
465 // match.setField( new MatchField(MatchType.NW_PROTO,
467 // match.setField( new MatchField(MatchType.TP_SRC,
470 // actions = new ArrayList<Action>();
471 // actions.add(new Controller());
473 // flow = new Flow(match, actions);
474 // flow.setIdleTimeout((short) 0);
475 // flow.setHardTimeout((short) 0);
476 // flow.setPriority( (short) 32769);
478 // // Modify the flow on the network node
479 // status = programmer.addFlow(node, flow);
481 // if (!status.isSuccess()) {
482 // log.warn("SDN Plugin failed to program the flow: {}. The failure is: {}",
483 // flow, status.getDescription());
486 // if(this.swManager != null){
487 // Set<NodeConnector> nconx =
488 // this.swManager.getNodeConnectors(node);
489 // log.info("got nconx");
500 public void updateNodeConnector(NodeConnector nodeConnector,
501 UpdateType type, Set<Property> props) {
506 public void hostListener(HostNodeConnector host) {
507 log.info("new host {}", host.getNetworkAddressAsString());
511 public List<Violator> getViolators() {
513 // select distinct request FROM dnsspy.bulkreply where data =
515 if (database.Connect()) {
516 return database.getViolators();
519 return new ArrayList<>();
523 public String setLocalDnsServer(String local_dns) {
525 // select distinct request FROM dnsspy.bulkreply where data =
527 if (database.Connect()) {
528 return database.setLocalDnsServer(local_dns);
531 return new String("Error updating data");
535 public String getLocalDnsServer() {
537 // select distinct request FROM dnsspy.bulkreply where data =
539 if (database.Connect()) {
540 return database.getLocalDnsServer();
543 return new String("Error getting data");
547 public List<DnsUsage> getExternalDnsUsage(int top) {
548 // select distinct request FROM dnsspy.bulkreply where data =
550 if (database.Connect()) {
552 return database.getExternalDnsUsage(top);
555 return new ArrayList<>();
559 public List<DnsRecordReply>getDatabaseDnsRecords(int limit, int offset) {
560 // TODO Auto-generated method stub
561 if (database.Connect()) {
563 return database.getDatabaseDnsRecords(limit, offset);
566 return new ArrayList<>();
570 public Boolean isConnected(){
572 if(database != null){
573 return database.isConnected();
580 public void savePropsAndConnect(Properties props){
582 BundleContext bundleContext = FrameworkUtil.getBundle(
583 this.getClass()).getBundleContext();
585 File foutput = bundleContext.getDataFile( dbconfig_filename );
586 OutputStream fostream;
589 fostream = new FileOutputStream(foutput);
592 props.store(fostream, "");
595 } catch ( IOException e) {
596 // TODO Auto-generated catch block