|
4 | 4 | # modify it under the terms of the GNU General Public License |
5 | 5 | # as published by the Free Software Foundation; either version 2 |
6 | 6 | # of the License, or (at your option) any later version. |
| 7 | +import json |
7 | 8 |
|
8 | 9 | # This program is distributed in the hope that it will be useful, |
9 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
22 | 23 | import multiprocessing |
23 | 24 | from typing import ( |
24 | 25 | List, |
| 26 | + Optional, |
25 | 27 | ) |
26 | 28 |
|
27 | 29 | import validators |
@@ -95,6 +97,8 @@ def init( |
95 | 97 | # is set by this proc to tell input proc that we are done |
96 | 98 | # processing and it can exit no issue |
97 | 99 | self.is_profiler_done_event = is_profiler_done_event |
| 100 | + self.gw_mac = None |
| 101 | + self.gw_ip = None |
98 | 102 |
|
99 | 103 | def read_configuration(self): |
100 | 104 | conf = ConfigParser() |
@@ -140,6 +144,87 @@ def get_rev_profile(self): |
140 | 144 | ) |
141 | 145 | return rev_profileid, rev_twid |
142 | 146 |
|
| 147 | + def get_gw_ip_using_gw_mac(self) -> Optional[str]: |
| 148 | + """ |
| 149 | + gets the ip of the given mac from the db |
| 150 | + prioritizes returning the ipv4. if not found, the function returns |
| 151 | + the ipv6. or none if both are not found. |
| 152 | + """ |
| 153 | + # the db returns a serialized list of IPs belonging to this mac |
| 154 | + gw_ips: str = self.db.get_ip_of_mac(self.gw_mac) |
| 155 | + |
| 156 | + if not gw_ips: |
| 157 | + return |
| 158 | + |
| 159 | + gw_ips: List[str] = json.loads(gw_ips) |
| 160 | + # try to get the ipv4 if found in that list |
| 161 | + for ip in gw_ips: |
| 162 | + try: |
| 163 | + ipaddress.IPv4Address(ip) |
| 164 | + return ip |
| 165 | + except ipaddress.AddressValueError: |
| 166 | + continue |
| 167 | + |
| 168 | + # all of them are ipv6, return the first |
| 169 | + return gw_ips[0] |
| 170 | + |
| 171 | + def is_gw_info_detected(self, info_type: str) -> bool: |
| 172 | + """ |
| 173 | + checks own attributes and the db for the gw mac/ip |
| 174 | + :param info_type: can be 'mac' or 'ip' |
| 175 | + """ |
| 176 | + info_mapping = { |
| 177 | + "mac": ("gw_mac", self.db.get_gateway_mac), |
| 178 | + "ip": ("gw_ip", self.db.get_gateway_ip), |
| 179 | + } |
| 180 | + |
| 181 | + if info_type not in info_mapping: |
| 182 | + raise ValueError(f"Unsupported info_type: {info_type}") |
| 183 | + |
| 184 | + attr, check_db_method = info_mapping[info_type] |
| 185 | + |
| 186 | + if getattr(self, attr): |
| 187 | + # the reason we don't just check the db is we don't want a db |
| 188 | + # call per each flow |
| 189 | + return True |
| 190 | + |
| 191 | + # did some other module manage to get it? |
| 192 | + if info := check_db_method(): |
| 193 | + setattr(self, attr, info) |
| 194 | + return True |
| 195 | + |
| 196 | + return False |
| 197 | + |
| 198 | + def get_gateway_info(self): |
| 199 | + """ |
| 200 | + Gets the IP and MAC of the gateway and stores them in the db |
| 201 | +
|
| 202 | + usually the mac of the flow going from a private ip -> a |
| 203 | + public ip is the mac of the GW |
| 204 | + """ |
| 205 | + gw_mac_found: bool = self.is_gw_info_detected("mac") |
| 206 | + if not gw_mac_found: |
| 207 | + if utils.is_private_ip( |
| 208 | + self.flow.saddr |
| 209 | + ) and not utils.is_ignored_ip(self.flow.daddr): |
| 210 | + self.gw_mac: str = self.flow.dmac |
| 211 | + self.db.set_default_gateway("MAC", self.gw_mac) |
| 212 | + self.print( |
| 213 | + f"MAC address of the gateway detected: " |
| 214 | + f"{green(self.gw_mac)}" |
| 215 | + ) |
| 216 | + gw_mac_found = True |
| 217 | + |
| 218 | + # we need the mac to be set to be able to find the ip using it |
| 219 | + if not self.is_gw_info_detected("ip") and gw_mac_found: |
| 220 | + self.gw_ip: Optional[str] = self.get_gw_ip_using_gw_mac() |
| 221 | + if self.gw_ip: |
| 222 | + self.db.set_default_gateway("IP", self.gw_ip) |
| 223 | + self.print( |
| 224 | + f"IP address of the gateway detected: " |
| 225 | + f"{green(self.gw_ip)}" |
| 226 | + ) |
| 227 | + |
143 | 228 | def add_flow_to_profile(self): |
144 | 229 | """ |
145 | 230 | This is the main function that takes the columns of a flow |
@@ -170,6 +255,8 @@ def add_flow_to_profile(self): |
170 | 255 | # software and weird.log flows are allowed to not have a daddr |
171 | 256 | return False |
172 | 257 |
|
| 258 | + self.get_gateway_info() |
| 259 | + |
173 | 260 | # Check if the flow is whitelisted and we should not process it |
174 | 261 | if self.whitelist.is_whitelisted_flow(self.flow): |
175 | 262 | return True |
|
0 commit comments