More features: Complete update, delete, support required access-list-params, reconnect after error for safty, Improved API generally and MtSentence-building

This commit is contained in:
Michael Hoess 2015-08-20 22:14:44 +01:00
parent 88f3594d5f
commit 587f031bf8
8 changed files with 233 additions and 58 deletions

View File

@ -3,8 +3,11 @@
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/2" lastBookmarkId="0"/> <editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/2" lastBookmarkId="0"/>
<open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/2"> <open-files xmlns="http://www.netbeans.org/ns/projectui-open-files/2">
<group> <group>
<file>file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/Utils.java</file>
<file>file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/ops/AccessListEntry.java</file>
<file>file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/ops/Wireless.java</file> <file>file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/ops/Wireless.java</file>
<file>file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/sample/TestApp.java</file> <file>file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/ApiConnection.java</file>
<file>file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/MtSentence.java</file>
<file>file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/ops/OPs.java</file> <file>file:/home/mh/Dvp/prj/MtUtils/MikroTikAPI/src/com/eightOceans/mikroTik/ops/OPs.java</file>
</group> </group>
</open-files> </open-files>

View File

@ -26,14 +26,12 @@ import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
/** /**
* *
* @author mh TODO: Improve listen/getData/readin * @author mh TODO: Improve listen/getData/readin
*/ */
public class ApiConnection { public class ApiConnection {
private Socket sock; private Socket sock;
private boolean isAuthenticated; private boolean isAuthenticated;
@ -53,11 +51,9 @@ public class ApiConnection {
return null; return null;
} }
public void checkClientTrusted(X509Certificate[] certs, String authType) { public void checkClientTrusted(X509Certificate[] certs, String authType) {
} }
public void checkServerTrusted(X509Certificate[] certs, String authType) { public void checkServerTrusted(X509Certificate[] certs, String authType) {
} }
} }
@ -68,7 +64,6 @@ public class ApiConnection {
// return true; // return true;
// } // }
// }; // };
private void ensureSockClosed() { private void ensureSockClosed() {
try { try {
if (sock != null) { if (sock != null) {
@ -84,13 +79,11 @@ public class ApiConnection {
} }
} }
private String getData() throws InterruptedException { private String getData() throws InterruptedException {
String s = (String) queue.take(); String s = (String) queue.take();
return s; return s;
} }
private void listen() { private void listen() {
if (this.isConnected()) { if (this.isConnected()) {
if (readCommand == null) { if (readCommand == null) {
@ -104,7 +97,6 @@ public class ApiConnection {
} }
} }
public Future<Result> close() { public Future<Result> close() {
return pool.submit(new Callable<Result>() { return pool.submit(new Callable<Result>() {
@ -119,17 +111,14 @@ public class ApiConnection {
}); });
} }
public boolean isAuthenticated() { public boolean isAuthenticated() {
return isAuthenticated; return isAuthenticated;
} }
public boolean isConnected() { public boolean isConnected() {
return sock != null; return sock != null;
} }
public Future<Result> login(String user, String password) { public Future<Result> login(String user, String password) {
return pool.submit(new Callable<Result>() { return pool.submit(new Callable<Result>() {
@ -190,7 +179,6 @@ public class ApiConnection {
); );
} }
public Future<Result> open(String host, int port, SSLMode sslMode) { public Future<Result> open(String host, int port, SSLMode sslMode) {
return pool.submit(new Callable<Result>() { return pool.submit(new Callable<Result>() {
@ -249,7 +237,6 @@ public class ApiConnection {
}); });
} }
/** /**
* sets and exectues command (sends it to RouterOS host connected) * sets and exectues command (sends it to RouterOS host connected)
* *
@ -261,7 +248,6 @@ public class ApiConnection {
return writeCommand.setCommand(s).runCommand(); return writeCommand.setCommand(s).runCommand();
} }
public Future<ReadDataResult> readData() { public Future<ReadDataResult> readData() {
return pool.submit(new Callable<ReadDataResult>() { return pool.submit(new Callable<ReadDataResult>() {
@ -272,7 +258,6 @@ public class ApiConnection {
}); });
} }
public ReadDataResult readDataSync() { public ReadDataResult readDataSync() {
try { try {
String g = getData(); 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;
}
} }

View File

@ -39,12 +39,10 @@ public class MtSentence {
} else { } else {
throw new UnsupportedOperationException("Unsupported word in rawString: " + w); throw new UnsupportedOperationException("Unsupported word in rawString: " + w);
} }
} }
if ((!s.isDone) /*&& (!s.isTrap)*/) { // if ((!s.isDone) /*&& (!s.isTrap)*/) {
s.words = newWords; s.words = newWords;
} //}
return s; return s;
} }

View File

@ -15,8 +15,11 @@ public class AccessListEntry {
e.forwarding = true; e.forwarding = true;
e.minSigStrength = -120; e.minSigStrength = -120;
e.maxSigStrength = 120; e.maxSigStrength = 120;
e.algo = "aes-ccm"; e.iface = "all";
e.privateKey = Utils.createPrivateWifiKey(); //e.algo = "aes-ccm";
//e.privateKey = Utils.createPrivateWifiKey();
e.algo = "none";
e.privateKey = "";
e.preSharedKey = Utils.createPresharedWifiKey(12,14,1,2); e.preSharedKey = Utils.createPresharedWifiKey(12,14,1,2);
return e; return e;
@ -24,13 +27,28 @@ public class AccessListEntry {
public String id; public String id;
public String comment; public String comment;
public String iface;
public String macAddress; public String macAddress;
public int minSigStrength; public int minSigStrength;
public int maxSigStrength; public int maxSigStrength;
public boolean forwarding; public boolean forwarding;
public boolean authentication; 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; 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; public String privateKey;
/**
* Insert password here, to override the one set in the
* wifi-security-profiles
*/
public String preSharedKey; public String preSharedKey;
public String managementProtectionKey; public String managementProtectionKey;
public boolean disabled; public boolean disabled;

View File

@ -39,8 +39,18 @@ public class Wireless {
sb.append("set\n"); sb.append("set\n");
} }
sb.append("=comment=" + e.comment + "\n"); sb.append("=comment=" + N2E(e.comment) + "\n");
sb.append("=mac-address=" + e.macAddress + "\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))) { if ((e.id != null) && (!"".equals(e.id))) {
sb.append("=.id=" + e.id + "\n"); sb.append("=.id=" + e.id + "\n");
@ -49,34 +59,52 @@ public class Wireless {
conn.sendCommand(sb.toString()); conn.sendCommand(sb.toString());
Result res = new Result(); Result res = conn.readUpdateReplySync();
ReadDataResult r = conn.readDataSync(); if (!res.Success) {
while (true) { res.ResultMessage = "AccessList update: " + res.ResultMessage;
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";
}
}
} }
return res; return res;
} }
}); });
} }
public Future<Result> remove(AccessListEntry e) {
return pool.submit(new Callable<Result>() {
@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<Result<List<AccessListEntry>>> getAccessListEntries() { public Future<Result<List<AccessListEntry>>> getAccessListEntries() {
return pool.submit(new Callable<Result<List<AccessListEntry>>>() { return pool.submit(new Callable<Result<List<AccessListEntry>>>() {
@ -104,6 +132,8 @@ public class Wireless {
} else if (ms.isDone()) { } else if (ms.isDone()) {
break; break;
} else { } else {
res.Success = false;
res.ResultMessage = "AccessList update failed, remote sent unexpected result";
/* /*
.id=*1B .id=*1B
=mac-address=00:00:00:00:00:01 =mac-address=00:00:00:00:00:01
@ -120,13 +150,16 @@ public class Wireless {
=disabled=false =disabled=false
=comment=#GEND0001# Vla =comment=#GEND0001# Vla
*/ */
AccessListEntry ale = AccessListEntry.createWithDefault(); AccessListEntry ale = AccessListEntry.createWithDefault();
ale.comment = ms.getWordAsMap().get("comment"); ale.comment = ms.getWordAsMap().get("comment");
ale.algo = ms.getWordAsMap().get("private-algo"); ale.algo = ms.getWordAsMap().get("private-algo");
ale.privateKey = ms.getWordAsMap().get("private-key");
ale.authentication = "true".equals(ms.getWordAsMap().get("authentication")); ale.authentication = "true".equals(ms.getWordAsMap().get("authentication"));
ale.disabled = "true".equals(ms.getWordAsMap().get("disabled")); ale.disabled = "true".equals(ms.getWordAsMap().get("disabled"));
ale.forwarding = "true".equals(ms.getWordAsMap().get("forwarding")); ale.forwarding = "true".equals(ms.getWordAsMap().get("forwarding"));
ale.id = ms.getWordAsMap().get(".id"); ale.id = ms.getWordAsMap().get(".id");
ale.iface = ms.getWordAsMap().get("interface");
ale.macAddress = ms.getWordAsMap().get("mac-address"); ale.macAddress = ms.getWordAsMap().get("mac-address");
ale.managementProtectionKey = ms.getWordAsMap().get("management-protection-key"); ale.managementProtectionKey = ms.getWordAsMap().get("management-protection-key");
String sigRange = ms.getWordAsMap().get("signal-range"); String sigRange = ms.getWordAsMap().get("signal-range");
@ -149,7 +182,6 @@ public class Wireless {
} }
} }
ale.preSharedKey = ms.getWordAsMap().get("private-pre-shared-key"); ale.preSharedKey = ms.getWordAsMap().get("private-pre-shared-key");
ale.privateKey = ms.getWordAsMap().get("private-key");
resLst.add(ale); resLst.add(ale);
} }
} }

View File

@ -3,8 +3,27 @@ AP_1_PORT=8729
AP_1_USER=rcfg AP_1_USER=rcfg
AP_1_PWPREF=0a 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=MK Iphone 7
DEV_1_MAC=01:01:01:01:01:02 DEV_1_MAC=01:01:01:01:01:02
DEV_1_KEY=saldk9012LLada DEV_1_KEY=saldk9012LLada
DEV_1_IFACE=wlan2
DEV_1_MINDB=-70 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

View File

@ -9,6 +9,7 @@ package com.eightOceans.mtWifiUserConfig;
public class DevDef { public class DevDef {
public int index; public int index;
public String name; public String name;
public String iface;
public String mac; public String mac;
public String preshareKey; public String preshareKey;
public int minDbAllowed; public int minDbAllowed;

View File

@ -19,6 +19,7 @@ import java.util.concurrent.TimeoutException;
* *
* @author mh * @author mh
*/ */
// Restliche Features: Multiple Iface support, Output-Optimierung
public class Main { public class Main {
private final static String VERS = "0.0.1"; private final static String VERS = "0.0.1";
@ -57,16 +58,29 @@ public class Main {
// load // load
i = 0; i = 0;
while (true) { int e = 0;
while (e < 100) {
i++; i++;
_currOp = "Checking Device " + i + " config"; _currOp = "Checking Device " + i + " config";
DevDef dev = new DevDef(); DevDef dev = new DevDef();
dev.index = i; dev.index = i;
dev.name = props.getProperty("DEV_" + i, null); dev.name = props.getProperty("DEV_" + i, null);
if (dev.name == 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); 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); dev.preshareKey = props.getProperty("DEV_" + i + "_KEY", null);
if ((dev.preshareKey == null) || (dev.preshareKey.length() < 8)) { if ((dev.preshareKey == null) || (dev.preshareKey.length() < 8)) {
throw new RuntimeException("Preshared key too short. must min 8 chars!"); throw new RuntimeException("Preshared key too short. must min 8 chars!");
@ -99,10 +113,13 @@ public class Main {
} catch (Exception ex) { } catch (Exception ex) {
System.out.println("! While " + _currOp + ": " + ex.getMessage()); System.out.println("! While " + _currOp + ": " + ex.getMessage());
System.out.println("* Done");
System.exit(1);
} }
_currOp = "Done"; _currOp = "Done";
System.out.println("* Done"); System.out.println("* Done");
System.exit(0);
} }
private static Properties loadProps() { private static Properties loadProps() {
@ -123,6 +140,7 @@ public class Main {
GETENTRIES, GETENTRIES,
UPDATEENTRIES, UPDATEENTRIES,
FAILED, FAILED,
RECONNECT,
EXIT, EXIT,
DONE DONE
} }
@ -140,12 +158,21 @@ public class Main {
OPs mtOps = new OPs(conn); OPs mtOps = new OPs(conn);
List<AccessListEntry> entries = null; List<AccessListEntry> entries = null;
List<AccessListEntry> toDel = new ArrayList();
List<AccessListEntry> toUpd = new ArrayList();
CommState cs = CommState.CONNECT; CommState cs = CommState.CONNECT;
while (cs != CommState.DONE) { while (cs != CommState.DONE) {
try { try {
Result r; Result r;
switch (cs) { switch (cs) {
case RECONNECT:
conn.close();
conn = new ApiConnection();
mtOps = new OPs(conn);
cs = CommState.CONNECT;
break;
case CONNECT: case CONNECT:
setCurrOp("Connecting to ap " + ap.host); setCurrOp("Connecting to ap " + ap.host);
r = conn.open(ap.host, ap.port, SSLMode.Weak).get(to, TimeUnit.SECONDS); 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); setCurrOp("Login to " + ap.host);
r = conn.login(ap.user, ap.pwPrefix + remPw).get(to, TimeUnit.SECONDS); r = conn.login(ap.user, ap.pwPrefix + remPw).get(to, TimeUnit.SECONDS);
if (r.Success) { if (r.Success) {
if (entries == null) {
cs = cs.GETENTRIES; cs = cs.GETENTRIES;
} else {
cs = CommState.UPDATEENTRIES;
}
} else { } else {
errMsg = "Login Failed: " + r.ResultMessage; errMsg = "Login Failed: " + r.ResultMessage;
cs = CommState.FAILED; cs = CommState.FAILED;
@ -171,6 +202,7 @@ public class Main {
Result<List<AccessListEntry>> getRes = mtOps.getWireless().getAccessListEntries().get(to, TimeUnit.SECONDS); Result<List<AccessListEntry>> getRes = mtOps.getWireless().getAccessListEntries().get(to, TimeUnit.SECONDS);
if (getRes.Success) { if (getRes.Success) {
entries = getRes.Result; entries = getRes.Result;
findChanges(devs, entries, toDel, toUpd);
cs = cs.UPDATEENTRIES; cs = cs.UPDATEENTRIES;
} else { } else {
errMsg = "Getting access entries failed: " + getRes.ResultMessage; errMsg = "Getting access entries failed: " + getRes.ResultMessage;
@ -178,19 +210,34 @@ public class Main {
} }
break; break;
case UPDATEENTRIES: case UPDATEENTRIES:
List<AccessListEntry> toDel = new ArrayList(); while (toUpd.size() > 0) {
List<AccessListEntry> toUpd = new ArrayList(); AccessListEntry e = toUpd.remove(0);
findChanges(devs, entries, toDel, toUpd);
for (AccessListEntry e : toUpd) {
setCurrOp("Updating '" + e.comment + "' on " + ap.host); setCurrOp("Updating '" + e.comment + "' on " + ap.host);
Result uRes = mtOps.getWireless().createOrUpdate(e).get(to, TimeUnit.SECONDS); Result uRes = mtOps.getWireless().createOrUpdate(e).get(to, TimeUnit.SECONDS);
if (!uRes.Success) { if (!uRes.Success) {
System.out.println(".... Update failed:" + uRes.ResultMessage); System.out.println(".... Update failed:" + uRes.ResultMessage);
cs = CommState.RECONNECT;
break;
} }
} }
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"); setCurrOp("Updating ended");
cs = CommState.EXIT; cs = CommState.EXIT;
}
break; break;
case FAILED: case FAILED:
System.out.println(" ! " + (errMsg != null ? errMsg : (_currOp + " Failed")) + "! Skipping this AP"); System.out.println(" ! " + (errMsg != null ? errMsg : (_currOp + " Failed")) + "! Skipping this AP");
cs = CommState.EXIT; cs = CommState.EXIT;
@ -261,6 +308,10 @@ public class Main {
t.preSharedKey = dev.preshareKey; t.preSharedKey = dev.preshareKey;
chg = true; chg = true;
} }
if (!Objects.equals(dev.iface, t.iface)) {
t.iface = dev.iface;
chg = true;
}
if (chg) { if (chg) {
toUpd.add(t); toUpd.add(t);