package cn.edu.sjtu.omnilab.syslogcleanser.wifilogfilter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeMap;
/**
* Main algorithm to extract user association session with AP.
* @author Wenjuan Gong
*
*/
public class SessionExtraction {
private final static String ACTION_AUTH_REQUEST = "AuthRequest";
private final static String ACTION_ASSOC_REQUEST = "AssocRequest";
private final static String ACTION_DEAUTH_REQUEST = "Deauth";
private final static String ACTION_DEASSOS_REQUEST = "Disassoc";
private final static String ACTION_IP_ALLOCATION = "IPAllocation";
private final static String ACTION_IP_RECYCLE = "IPRecycle";
private final static String ACTION_USER_AUTH = "UserAuth";
private final static String ACTION_SESSION_START = "auth";
private final static String ACTION_SESSION_END = "deauth";
private final static String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
/**
* Main portal of user session extraction algorithm
* @param individualRawLines The filtered log messages for an individual user.
* @return The sessions extracted.
* @throws IOException
* @throws ParseException
*/
public static List<String> extractSessions(List<String> individualRawLines) throws IOException, ParseException{
List<String> allLines = new LinkedList<String>();
// sort lines
// Note: we should order the logs with respect to timestamp only, not with the action (bug).
Collections.sort(allLines, new Comparator<String>() {
public int compare(String o1, String o2) {
return compare(o1.split("\\t")[1], o2.split("\\t")[1]);
}
});
// filter session info
allLines = sessionInfoFilter(individualRawLines);
// extract rough sessions
allLines = extractRoughSessions(allLines);
// adjust end times
allLines = mergeSessions(adjustEndTime(allLines));
return allLines;
}
/**
* Filter out invalid and duplicate messages
* @param allLines
* @return
*/
private static List<String> sessionInfoFilter(List<String> allLines){
List<String> filterLines = new LinkedList<String>();
String auth_previous_time = "";
String auth_previous_ap = "";
String deauth_previous_time = "";
String deauth_previous_ap = "";
for ( String line : allLines ){
String[] parts = line.split("\\t");
if ( parts.length < 4 )
continue;
String mac = parts[0];
String time = parts[1];
String action = parts[2];
String ap = parts[3];
if(action.contains(ACTION_AUTH_REQUEST) || action.contains(ACTION_ASSOC_REQUEST)){
if(time.equals(auth_previous_time) && ap.equals(auth_previous_ap)){
// filter out duplicate messages
continue;
} else{
/*
* line format:
* usermac, timestamp, <Auth, Deauth>, apname
*/
filterLines.add(mac + "\t" + time + "\t" + ACTION_SESSION_START + "\t" + ap);
auth_previous_time = time;
auth_previous_ap = ap;
}
} else if (action.contains(ACTION_DEAUTH_REQUEST) || action.contains(ACTION_DEASSOS_REQUEST)) {
if(time.equals(deauth_previous_time) && ap.equals(deauth_previous_ap)){
continue;
} else{
/*
* line format:
* usermac, timestamp, <Auth, Deauth>, apname
*/
filterLines.add(mac + "\t" + time + "\t" + ACTION_SESSION_END + "\t" + ap);
deauth_previous_time = time;
deauth_previous_ap = ap;
}
} else{
/*
* Remain the content of other lines
*/
filterLines.add(line);
}
}
return filterLines;
}
private static List<String> extractRoughSessions(List<String> allLines) throws ParseException {
List<String> roughSessions = new LinkedList<String>();
//prepare to store the "Auth" log, K: ap name , V: date_string
TreeMap<String, String> ap_auth_tmap = new TreeMap<String, String>();
//start exacting the movement session
String previous_ap = "";
String previous_action = "";
for ( String line : allLines ){
String[] parts = line.split("\\t");
if ( parts.length < 4 )
continue;
String mac = parts[0];
String date_string = parts[1];
String action = parts[2];
String ap = parts[3];
if(action.equals(ACTION_SESSION_START)){ //auth request log
if(ap_auth_tmap.containsKey(ap)){
//the treemap already has the ap name
if(ap.equals(previous_ap) && action.equals(previous_action)){
//discard
if (Utils.debug){
System.out.println("repeating auth log!");
}
continue;
} else{
// replace the previous one
// currently, we do not end an auth (without deauth) with another auth.
ap_auth_tmap.put(ap, date_string);
}
}
else{
//a new ap name
ap_auth_tmap.put(ap, date_string);
}
} else if (action.equals(ACTION_SESSION_END)){ //"Deauth log"
if(ap_auth_tmap.containsKey(ap)){
//movement exact
String start_time_string = ap_auth_tmap.get(ap);
String end_time_string = date_string;
DateFormat df = new SimpleDateFormat(DATE_TIME_FORMAT);
Date start_time = df.parse(start_time_string);
Date end_time = df.parse(end_time_string);
long duration = (end_time.getTime() - start_time.getTime())/1000;
/*
* Write into the output file in the format of
* USER_MAC, START_TIME, END_TIME, DURATION, AP_NAME
*/
roughSessions.add(mac + "\t" + start_time_string + "\t" + end_time_string + "\t" + duration + "\t" + ap);
//remove the auth time with this ap in the ap_auth_tmap
ap_auth_tmap.remove(ap);
} else{
//discard
if (Utils.debug) {
System.out.println("Can not find the ap! discard!!!");
}
continue;
}
} else {
/*
* Add other lines as what they are for future usage.
*/
roughSessions.add(line);
}
previous_ap = ap;
previous_action = action;
}
return roughSessions;
}
private static List<String> adjustEndTime(List<String> allLines) throws ParseException {
List<String> finalLines = new LinkedList<String>();
if( allLines.size() == 0 ){
//an empty file
return finalLines;
}
DateFormat df = new SimpleDateFormat(DATE_TIME_FORMAT);
String previous_line = "";
String previous_enddate_string = "";
int last_read = 0;
for ( last_read = 0; last_read < allLines.size(); last_read++){
previous_line = allLines.get(last_read);
if(previous_line.contains(ACTION_IP_ALLOCATION)||previous_line.contains(ACTION_IP_RECYCLE)||previous_line.contains(ACTION_USER_AUTH)){
finalLines.add(previous_line);
continue;
} else {
previous_enddate_string = previous_line.split("\\t")[2];
break;
}
}
for ( int i = last_read+1; i < allLines.size(); i++ ) {
String line = allLines.get(i);
if(line.contains(ACTION_IP_ALLOCATION)||line.contains(ACTION_IP_RECYCLE)||line.contains(ACTION_USER_AUTH)){
finalLines.add(line);
continue;
}
String startdate_string = line.split("\\t")[1];
String enddate_string = line.split("\\t")[2];
Date startdate = df.parse(startdate_string);
Date previous_enddate = df.parse(previous_enddate_string);
long diff = previous_enddate.getTime() - startdate.getTime();
if(diff <= 0){
//write the previous line into the output_file
finalLines.add(previous_line);
}
else{
//change the end time of the previous line to the start time of the following line , and write into output file
String write_line = previous_line.replace(previous_enddate_string, startdate_string);
finalLines.add(write_line);
}
previous_line = line;
previous_enddate_string = enddate_string;
}
//write the last line into the output file
if(previous_line != null)
finalLines.add(previous_line);
return finalLines;
}
private static List<String> mergeSessions(List<String> allLines) throws ParseException {
List<String> finalLines = new LinkedList<String>();
DateFormat df = new SimpleDateFormat(DATE_TIME_FORMAT);
String previous_line = "";
String previous_mac = "";
String previous_startdate_string = "";
String previous_enddate_string = "";
String previous_duration_string = "";
String previous_ap = "";
long duration = 0;
int last_read = 0;
if( allLines.size() != 0 ){
previous_line = allLines.get(0);
if(previous_line.contains(ACTION_IP_ALLOCATION)||previous_line.contains(ACTION_IP_RECYCLE)||previous_line.contains(ACTION_USER_AUTH)){
finalLines.add(previous_line);
for ( int i = 1; i<allLines.size(); i++){
previous_line = allLines.get(i);
last_read = i;
if(previous_line.contains(ACTION_IP_ALLOCATION)||previous_line.contains(ACTION_IP_RECYCLE)||previous_line.contains(ACTION_USER_AUTH)){
finalLines.add(previous_line);
continue;
} else {
String[] parts = previous_line.split("\\t");
if ( parts.length < 5 )
continue;
previous_mac = parts[0];
previous_startdate_string = parts[1];
previous_enddate_string= parts[2];
previous_duration_string = parts[3];
previous_ap = parts[4];
duration = Long.parseLong(previous_duration_string);
break;
}
}
}
else{
String[] parts = previous_line.split("\\t");
if ( parts.length == 5 ){
previous_mac = parts[0];
previous_startdate_string = parts[1];
previous_enddate_string= parts[2];
previous_duration_string = parts[3];
previous_ap = parts[4];
duration = Long.parseLong(previous_duration_string);
}
}
}
else //an empty file
return finalLines;
String line ="";
for ( int i=last_read+1; i<allLines.size(); i++ ) {
line = allLines.get(i);
if(line.contains(ACTION_IP_ALLOCATION)||line.contains(ACTION_IP_RECYCLE)||line.contains(ACTION_USER_AUTH)){
finalLines.add(line);
continue;
}
String[] parts = line.split("\\t");
if ( parts.length < 5 )
continue;
String startdate_string = parts[1];
String enddate_string = parts[2];
String duration_string = parts[3];
String ap = parts[4];
Date previous_enddate = df.parse(previous_enddate_string);
Date startdate = df.parse(startdate_string);
long diff = (startdate.getTime() - previous_enddate.getTime())/1000;
long current_duration = Long.parseLong(duration_string);
if(ap.equals(previous_ap) && diff <= 10){
//merge
previous_enddate_string = enddate_string;
duration += (current_duration + diff);
}
else{
//write the session into the output file
finalLines.add(previous_mac + "\t" + previous_startdate_string + "\t" + previous_enddate_string + "\t" + duration + "\t" + previous_ap);
//update
previous_ap = ap;
previous_startdate_string = startdate_string;
previous_enddate_string = enddate_string;
duration = current_duration;
}
}
if(!previous_startdate_string.equals(""))
finalLines.add(previous_mac + "\t" + previous_startdate_string + "\t" + previous_enddate_string + "\t" + duration + "\t" + previous_ap);
return finalLines;
}
}