From 587f031bf8045a066d02e495a6404a065fca1905 Mon Sep 17 00:00:00 2001 From: mh Date: Thu, 20 Aug 2015 22:14:44 +0100 Subject: [PATCH] More features: Complete update, delete, support required access-list-params, reconnect after error for safty, Improved API generally and MtSentence-building --- MikroTikAPI/nbproject/private/private.xml | 5 +- .../eightOceans/mikroTik/ApiConnection.java | 83 ++++++++++++++---- .../com/eightOceans/mikroTik/MtSentence.java | 6 +- .../mikroTik/ops/AccessListEntry.java | 22 ++++- .../eightOceans/mikroTik/ops/Wireless.java | 84 +++++++++++++------ MtWifiUserConfig/config.properties | 19 +++++ .../eightOceans/mtWifiUserConfig/DevDef.java | 1 + .../eightOceans/mtWifiUserConfig/Main.java | 71 +++++++++++++--- 8 files changed, 233 insertions(+), 58 deletions(-) diff --git a/MikroTikAPI/nbproject/private/private.xml b/MikroTikAPI/nbproject/private/private.xml index 6b5984a..e5c84c3 100644 --- a/MikroTikAPI/nbproject/private/private.xml +++ b/MikroTikAPI/nbproject/private/private.xml @@ -3,8 +3,11 @@ + file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/Utils.java + file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/ops/AccessListEntry.java file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/ops/Wireless.java - file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/sample/TestApp.java + file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/ApiConnection.java + file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/MtSentence.java file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/ops/OPs.java diff --git a/MikroTikAPI/src/com/eightOceans/mikroTik/ApiConnection.java b/MikroTikAPI/src/com/eightOceans/mikroTik/ApiConnection.java index c2fd085..304efab 100644 --- a/MikroTikAPI/src/com/eightOceans/mikroTik/ApiConnection.java +++ b/MikroTikAPI/src/com/eightOceans/mikroTik/ApiConnection.java @@ -26,14 +26,12 @@ import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; - /** * * @author mh TODO: Improve listen/getData/readin */ public class ApiConnection { - private Socket sock; private boolean isAuthenticated; @@ -53,11 +51,9 @@ public class ApiConnection { return null; } - public void checkClientTrusted(X509Certificate[] certs, String authType) { } - public void checkServerTrusted(X509Certificate[] certs, String authType) { } } @@ -68,7 +64,6 @@ public class ApiConnection { // return true; // } // }; - private void ensureSockClosed() { try { if (sock != null) { @@ -84,13 +79,11 @@ public class ApiConnection { } } - private String getData() throws InterruptedException { String s = (String) queue.take(); return s; } - private void listen() { if (this.isConnected()) { if (readCommand == null) { @@ -104,7 +97,6 @@ public class ApiConnection { } } - public Future close() { return pool.submit(new Callable() { @@ -119,17 +111,14 @@ public class ApiConnection { }); } - public boolean isAuthenticated() { return isAuthenticated; } - public boolean isConnected() { return sock != null; } - public Future login(String user, String password) { return pool.submit(new Callable() { @@ -190,7 +179,6 @@ public class ApiConnection { ); } - public Future open(String host, int port, SSLMode sslMode) { return pool.submit(new Callable() { @@ -249,7 +237,6 @@ public class ApiConnection { }); } - /** * sets and exectues command (sends it to RouterOS host connected) * @@ -261,7 +248,6 @@ public class ApiConnection { return writeCommand.setCommand(s).runCommand(); } - public Future readData() { return pool.submit(new Callable() { @@ -272,7 +258,6 @@ public class ApiConnection { }); } - public ReadDataResult readDataSync() { try { String g = getData(); @@ -288,5 +273,73 @@ public class ApiConnection { } } + public static interface IReadGetReplyCb { + /** + * Sentence with data (getWords()) + * @param dataSentence + */ + public void handle(MtSentence dataSentence); + } + + /** + * Read the reply to an find/get command + * @return Result + */ + public Result readGetReplySync(IReadGetReplyCb cb) { + Result res = new Result(); + res.Success = true; + ReadDataResult r = readDataSync(); + while (true) { + if (!r.Success) { + res.Success = false; + res.ResultMessage = "Error reading reply" + r.ResultMessage; + break; + } else { + res.Success = true; + MtSentence ms = MtSentence.from(r.Data); + if (ms.isTrap()) { + res.Success = false; + res.ResultMessage = "Remote sent trap: " + ms.getTrapMsg(); + break; + } else if (ms.isDone()) { + break; + } else { + cb.handle(ms); + } + } + } + return res; + } + + + /** + * Read the reply to an set/add/delete command + * @return Result + */ + public Result readUpdateReplySync() { + Result res = new Result(); + res.Success = true; + ReadDataResult r = readDataSync(); + while (true) { + if (!r.Success) { + res.Success = false; + res.ResultMessage = "Error reading reply" + r.ResultMessage; + break; + } else { + MtSentence ms = MtSentence.from(r.Data); + if (ms.isTrap()) { + res.Success = false; + res.ResultMessage = "Failed, remote sent trap: " + ms.getTrapMsg(); + break; + } else if (ms.isDone()) { + break; + } else { + res.Success = false; + res.ResultMessage = "Failed, remote sent unexpected result"; + } + } + } + return res; + } } diff --git a/MikroTikAPI/src/com/eightOceans/mikroTik/MtSentence.java b/MikroTikAPI/src/com/eightOceans/mikroTik/MtSentence.java index 0e374fd..f8fb04b 100644 --- a/MikroTikAPI/src/com/eightOceans/mikroTik/MtSentence.java +++ b/MikroTikAPI/src/com/eightOceans/mikroTik/MtSentence.java @@ -39,12 +39,10 @@ public class MtSentence { } else { throw new UnsupportedOperationException("Unsupported word in rawString: " + w); } - - } - if ((!s.isDone) /*&& (!s.isTrap)*/) { + // if ((!s.isDone) /*&& (!s.isTrap)*/) { s.words = newWords; - } + //} return s; } diff --git a/MikroTikAPI/src/com/eightOceans/mikroTik/ops/AccessListEntry.java b/MikroTikAPI/src/com/eightOceans/mikroTik/ops/AccessListEntry.java index 057be4a..3566fd2 100644 --- a/MikroTikAPI/src/com/eightOceans/mikroTik/ops/AccessListEntry.java +++ b/MikroTikAPI/src/com/eightOceans/mikroTik/ops/AccessListEntry.java @@ -15,8 +15,11 @@ public class AccessListEntry { e.forwarding = true; e.minSigStrength = -120; e.maxSigStrength = 120; - e.algo = "aes-ccm"; - e.privateKey = Utils.createPrivateWifiKey(); + e.iface = "all"; + //e.algo = "aes-ccm"; + //e.privateKey = Utils.createPrivateWifiKey(); + e.algo = "none"; + e.privateKey = ""; e.preSharedKey = Utils.createPresharedWifiKey(12,14,1,2); return e; @@ -24,13 +27,28 @@ public class AccessListEntry { public String id; public String comment; + public String iface; public String macAddress; public int minSigStrength; public int maxSigStrength; public boolean forwarding; public boolean authentication; + /** + * Unclear purpose, not used in our scenario. Propably for old-style WEP. + * We enforce strong crypto via the wifi-security-profiles, + *and override only the specific password per client + */ public String algo; + /** + * Unclear purpose, not used in our scenario. Propably for old-style WEP. + * We enforce strong crypto via the wifi-security-profiles, + *and override only the specific password per client + */ public String privateKey; + /** + * Insert password here, to override the one set in the + * wifi-security-profiles + */ public String preSharedKey; public String managementProtectionKey; public boolean disabled; diff --git a/MikroTikAPI/src/com/eightOceans/mikroTik/ops/Wireless.java b/MikroTikAPI/src/com/eightOceans/mikroTik/ops/Wireless.java index 463d962..2485264 100644 --- a/MikroTikAPI/src/com/eightOceans/mikroTik/ops/Wireless.java +++ b/MikroTikAPI/src/com/eightOceans/mikroTik/ops/Wireless.java @@ -39,8 +39,18 @@ public class Wireless { sb.append("set\n"); } - sb.append("=comment=" + e.comment + "\n"); - sb.append("=mac-address=" + e.macAddress + "\n"); + sb.append("=comment=" + N2E(e.comment) + "\n"); + sb.append("=authentication=" + (e.authentication ? "true" : "false") + "\n"); + + sb.append("=disabled=" + (e.disabled ? "true" : "false") + "\n"); + sb.append("=forwarding=" + (e.forwarding ? "true" : "false") + "\n"); + sb.append("=interface=" + N2E(e.iface) + "\n"); + sb.append("=mac-address=" + N2E(e.macAddress) + "\n"); + sb.append("=management-protection-key=" + N2E(e.managementProtectionKey) + "\n"); + sb.append("=signal-range=" + e.minSigStrength + ".." + e.maxSigStrength + "\n"); + sb.append("=private-pre-shared-key=" + N2E(e.preSharedKey) + "\n"); + sb.append("=private-algo=" + N2E(e.algo) + "\n"); + sb.append("=private-key=" + N2E(e.privateKey) + "\n"); if ((e.id != null) && (!"".equals(e.id))) { sb.append("=.id=" + e.id + "\n"); @@ -49,34 +59,52 @@ public class Wireless { conn.sendCommand(sb.toString()); - Result res = new Result(); - ReadDataResult r = conn.readDataSync(); - while (true) { - if (!r.Success) { - res.Success = false; - res.ResultMessage = "AccessList update failed: " + r.ResultMessage; - break; - } else { - MtSentence ms = MtSentence.from(r.Data); - if (ms.isTrap()) { - res.Success = false; - res.ResultMessage = "AccessList update failed, remote sent trap: " + ms.getTrapMsg(); - break; - } else if (ms.isDone()) { - res.Success = true; - break; - } else { - res.Success = false; - res.ResultMessage = "AccessList update failed, remote sent unexpected result"; - } - - } + Result res = conn.readUpdateReplySync(); + if (!res.Success) { + res.ResultMessage = "AccessList update: " + res.ResultMessage; } return res; } }); } + public Future remove(AccessListEntry e) { + + return pool.submit(new Callable() { + + @Override + public Result call() throws Exception { + + StringBuilder sb = new StringBuilder(); + sb.append("/interface/wireless/access-list/"); + if ((e.id == null) || ("".equals(e.id))) { + throw new IllegalArgumentException("Can't del entry w/o ID"); + } else { + sb.append("remove\n"); + } + + sb.append("=.id=" + e.id + "\n"); + + sb.append("!done"); + + conn.sendCommand(sb.toString()); + + Result res = conn.readUpdateReplySync(); + if (!res.Success) { + res.ResultMessage = "AccessList delete: " + res.ResultMessage; + } + return res; + } + }); + } + + private static String N2E(String s) { + if (s == null) { + return ""; + } + return s; + } + public Future>> getAccessListEntries() { return pool.submit(new Callable>>() { @@ -104,6 +132,8 @@ public class Wireless { } else if (ms.isDone()) { break; } else { + res.Success = false; + res.ResultMessage = "AccessList update failed, remote sent unexpected result"; /* .id=*1B =mac-address=00:00:00:00:00:01 @@ -120,13 +150,16 @@ public class Wireless { =disabled=false =comment=#GEND0001# Vla */ + AccessListEntry ale = AccessListEntry.createWithDefault(); ale.comment = ms.getWordAsMap().get("comment"); ale.algo = ms.getWordAsMap().get("private-algo"); + ale.privateKey = ms.getWordAsMap().get("private-key"); ale.authentication = "true".equals(ms.getWordAsMap().get("authentication")); ale.disabled = "true".equals(ms.getWordAsMap().get("disabled")); ale.forwarding = "true".equals(ms.getWordAsMap().get("forwarding")); ale.id = ms.getWordAsMap().get(".id"); + ale.iface = ms.getWordAsMap().get("interface"); ale.macAddress = ms.getWordAsMap().get("mac-address"); ale.managementProtectionKey = ms.getWordAsMap().get("management-protection-key"); String sigRange = ms.getWordAsMap().get("signal-range"); @@ -148,8 +181,7 @@ public class Wireless { break; } } - ale.preSharedKey = ms.getWordAsMap().get("private-pre-shared-key"); - ale.privateKey = ms.getWordAsMap().get("private-key"); + ale.preSharedKey = ms.getWordAsMap().get("private-pre-shared-key"); resLst.add(ale); } } diff --git a/MtWifiUserConfig/config.properties b/MtWifiUserConfig/config.properties index 862bce9..4292961 100644 --- a/MtWifiUserConfig/config.properties +++ b/MtWifiUserConfig/config.properties @@ -3,8 +3,27 @@ AP_1_PORT=8729 AP_1_USER=rcfg AP_1_PWPREF=0a +#AP_2=192.168.61.220 +#AP_2_PORT=8729 +#AP_2_USER=rcfg +#AP_2_PWPREF=0a + DEV_1=MK Iphone 7 DEV_1_MAC=01:01:01:01:01:02 DEV_1_KEY=saldk9012LLada +DEV_1_IFACE=wlan2 DEV_1_MINDB=-70 + +DEV_2=MH IPAD +DEV_2_MAC=9C:04:EB:98:A5:67 +DEV_2_KEY=lalalala +DEV_2_IFACE=wlan2 +DEV_2_MINDB=-73 + +DEV_3=MH Notebook +DEV_3_MAC=03:01:01:01:01:08 +DEV_3_KEY=lalalala +DEV_3_IFACE=wlan2 +DEV_3_MINDB=-75 + diff --git a/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/DevDef.java b/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/DevDef.java index 2a03100..c4e30fb 100644 --- a/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/DevDef.java +++ b/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/DevDef.java @@ -9,6 +9,7 @@ package com.eightOceans.mtWifiUserConfig; public class DevDef { public int index; public String name; + public String iface; public String mac; public String preshareKey; public int minDbAllowed; diff --git a/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/Main.java b/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/Main.java index ba2704f..2a56e35 100644 --- a/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/Main.java +++ b/MtWifiUserConfig/src/com/eightOceans/mtWifiUserConfig/Main.java @@ -19,6 +19,7 @@ import java.util.concurrent.TimeoutException; * * @author mh */ +// Restliche Features: Multiple Iface support, Output-Optimierung public class Main { private final static String VERS = "0.0.1"; @@ -57,16 +58,29 @@ public class Main { // load i = 0; - while (true) { + int e = 0; + while (e < 100) { i++; _currOp = "Checking Device " + i + " config"; DevDef dev = new DevDef(); dev.index = i; dev.name = props.getProperty("DEV_" + i, null); if (dev.name == null) { - break; + e++; + continue; + } + e = 0; + if (dev.name.length() > 16) { + throw new RuntimeException("Length of naem must be 1-16"); } dev.mac = props.getProperty("DEV_" + i + "_MAC", null); + if ((dev.mac == null) || (dev.mac.length() != 3 * 6 - 1)) { + throw new RuntimeException("Length of mac must be 17"); + } + dev.iface = props.getProperty("DEV_" + i + "_IFACE", null); + if ((dev.iface == null) || (dev.iface.length() > 16)) { + throw new RuntimeException("Length of iface must be 1-16"); + } dev.preshareKey = props.getProperty("DEV_" + i + "_KEY", null); if ((dev.preshareKey == null) || (dev.preshareKey.length() < 8)) { throw new RuntimeException("Preshared key too short. must min 8 chars!"); @@ -99,10 +113,13 @@ public class Main { } catch (Exception ex) { System.out.println("! While " + _currOp + ": " + ex.getMessage()); + System.out.println("* Done"); + System.exit(1); } _currOp = "Done"; System.out.println("* Done"); + System.exit(0); } private static Properties loadProps() { @@ -123,6 +140,7 @@ public class Main { GETENTRIES, UPDATEENTRIES, FAILED, + RECONNECT, EXIT, DONE } @@ -140,12 +158,21 @@ public class Main { OPs mtOps = new OPs(conn); List entries = null; + List toDel = new ArrayList(); + List toUpd = new ArrayList(); + CommState cs = CommState.CONNECT; while (cs != CommState.DONE) { try { Result r; switch (cs) { + case RECONNECT: + conn.close(); + conn = new ApiConnection(); + mtOps = new OPs(conn); + cs = CommState.CONNECT; + break; case CONNECT: setCurrOp("Connecting to ap " + ap.host); r = conn.open(ap.host, ap.port, SSLMode.Weak).get(to, TimeUnit.SECONDS); @@ -160,7 +187,11 @@ public class Main { setCurrOp("Login to " + ap.host); r = conn.login(ap.user, ap.pwPrefix + remPw).get(to, TimeUnit.SECONDS); if (r.Success) { - cs = cs.GETENTRIES; + if (entries == null) { + cs = cs.GETENTRIES; + } else { + cs = CommState.UPDATEENTRIES; + } } else { errMsg = "Login Failed: " + r.ResultMessage; cs = CommState.FAILED; @@ -171,6 +202,7 @@ public class Main { Result> getRes = mtOps.getWireless().getAccessListEntries().get(to, TimeUnit.SECONDS); if (getRes.Success) { entries = getRes.Result; + findChanges(devs, entries, toDel, toUpd); cs = cs.UPDATEENTRIES; } else { errMsg = "Getting access entries failed: " + getRes.ResultMessage; @@ -178,19 +210,34 @@ public class Main { } break; case UPDATEENTRIES: - List toDel = new ArrayList(); - List toUpd = new ArrayList(); - findChanges(devs, entries, toDel, toUpd); - for (AccessListEntry e : toUpd) { - setCurrOp("Updating '" + e.comment + "' on " + ap.host); + while (toUpd.size() > 0) { + AccessListEntry e = toUpd.remove(0); + setCurrOp("Updating '" + e.comment + "' on " + ap.host); Result uRes = mtOps.getWireless().createOrUpdate(e).get(to, TimeUnit.SECONDS); if (!uRes.Success) { System.out.println(".... Update failed:" + uRes.ResultMessage); + cs = CommState.RECONNECT; + break; } } - setCurrOp("Updating ended"); - cs = CommState.EXIT; + if (cs != CommState.RECONNECT) { + while (toDel.size() > 0) { + AccessListEntry e = toDel.remove(0); + setCurrOp("Deleting '" + e.comment + "' on " + ap.host); + Result uRes = mtOps.getWireless().remove(e).get(to, TimeUnit.SECONDS); + if (!uRes.Success) { + System.out.println(".... Delete failed:" + uRes.ResultMessage); + cs = CommState.RECONNECT; + break; + } + } + } + if (cs != CommState.RECONNECT) { + setCurrOp("Updating ended"); + cs = CommState.EXIT; + } break; + case FAILED: System.out.println(" ! " + (errMsg != null ? errMsg : (_currOp + " Failed")) + "! Skipping this AP"); cs = CommState.EXIT; @@ -261,6 +308,10 @@ public class Main { t.preSharedKey = dev.preshareKey; chg = true; } + if (!Objects.equals(dev.iface, t.iface)) { + t.iface = dev.iface; + chg = true; + } if (chg) { toUpd.add(t);