diff --git a/ip_config.json b/ip_config.json index e27fcc8..679b3a2 100644 --- a/ip_config.json +++ b/ip_config.json @@ -1 +1 @@ -{"last_interface": "Ethernet", "last_ip_prefix": "10.1.20", "previous_config": {"name": "Ethernet", "ip_address": "10.1.20.249", "subnet_mask": "255.240.0.0", "gateway": "10.1.20.1", "dhcp_enabled": false}} \ No newline at end of file +{"last_interface": "Ethernet", "last_ip_prefix": "10.1.33", "previous_config": {"name": "Ethernet", "ip_address": "192.168.88.249", "subnet_mask": "255.255.255.0", "gateway": "192.168.88.1", "dhcp_enabled": false}, "last_subnet_mask": "255.240.0.0"} \ No newline at end of file diff --git a/ip_history.json b/ip_history.json index b3c3d9b..bb01819 100644 --- a/ip_history.json +++ b/ip_history.json @@ -1 +1 @@ -["10.1.22", "169.254.38", "10.1.20", "10.146.76", "192.168.0", "10.101.8", "10.1.33", "10.1.92", "192.168.121", "192.168.1", "169.254.3", "10.202.4", "192.168.2", "10.1.30", "192.168.88"] \ No newline at end of file +[{"prefix": "10.1.33", "mask": "255.240.0.0"}, {"prefix": "192.168.88", "mask": "255.255.255.0"}, {"prefix": "192.168.199", "mask": "255.255.255.0"}, {"prefix": "192.168.200", "mask": "255.255.255.0"}, {"prefix": "10.202.4", "mask": "255.255.0.0"}, {"prefix": "169.254.38", "mask": "255.255.0.0"}, {"prefix": "192.168.1", "mask": "255.255.255.0"}, {"prefix": "169.254.69", "mask": "255.255.0.0"}, {"prefix": "192.168.212", "mask": "255.255.255.0"}, {"prefix": "10.1.20", "mask": "255.240.0.0"}, {"prefix": "10.1.22", "mask": "255.255.255.0"}, {"prefix": "10.146.76", "mask": "255.255.255.0"}, {"prefix": "192.168.0", "mask": "255.255.255.0"}, {"prefix": "10.101.8", "mask": "255.255.255.0"}, {"prefix": "10.1.92", "mask": "255.255.255.0"}] \ No newline at end of file diff --git a/menu-ip-change.py b/menu-ip-change.py index 9bee1ba..f9c00e5 100644 --- a/menu-ip-change.py +++ b/menu-ip-change.py @@ -90,15 +90,21 @@ class IPChangeConfig: self.last_interface = config.get("last_interface", "") self.last_ip_prefix = config.get("last_ip_prefix", "") self.previous_config = config.get("previous_config", {}) + # Add support for last used subnet mask + self.last_subnet_mask = config.get( + "last_subnet_mask", "255.255.255.0" + ) else: self.last_interface = "" self.last_ip_prefix = "" self.previous_config = {} + self.last_subnet_mask = "255.255.255.0" except Exception as e: print(f"Error loading config: {e}") self.last_interface = "" self.last_ip_prefix = "" self.previous_config = {} + self.last_subnet_mask = "255.255.255.0" def save_config(self): try: @@ -106,6 +112,8 @@ class IPChangeConfig: "last_interface": self.last_interface, "last_ip_prefix": self.last_ip_prefix, "previous_config": self.previous_config, + # Save last used subnet mask + "last_subnet_mask": self.last_subnet_mask, } with open(self.config_file, "w") as f: json.dump(config, f) @@ -435,6 +443,39 @@ class IPChangerApp: self.cidr_entry = ttk.Entry(subnet_frame, textvariable=self.cidr_bits, width=5) self.cidr_entry.pack(side=tk.LEFT) + # Add CIDR preset buttons - Fix: Create a standalone function for each button + cidr_presets_frame = ttk.Frame(subnet_frame) + cidr_presets_frame.pack(side=tk.LEFT, padx=5) + + # Fix: Use individual buttons with direct command references, not lambda + ttk.Button( + cidr_presets_frame, + text="/8", + width=4, + command=lambda: self.set_cidr_preset(8), + ).pack(side=tk.LEFT, padx=2) + + ttk.Button( + cidr_presets_frame, + text="/12", + width=4, + command=lambda: self.set_cidr_preset(12), + ).pack(side=tk.LEFT, padx=2) + + ttk.Button( + cidr_presets_frame, + text="/16", + width=4, + command=lambda: self.set_cidr_preset(16), + ).pack(side=tk.LEFT, padx=2) + + ttk.Button( + cidr_presets_frame, + text="/24", + width=4, + command=lambda: self.set_cidr_preset(24), + ).pack(side=tk.LEFT, padx=2) + # Sección de historial self.history_frame = ttk.LabelFrame( self.control_frame, text="IP History", padding="5" @@ -722,7 +763,16 @@ class IPChangerApp: try: if os.path.exists(self.config.history_file): with open(self.config.history_file, "r") as f: - self.ip_history = json.load(f) + history_data = json.load(f) + # Check if history is in old format (just list of IPs) + if history_data and isinstance(history_data[0], str): + # Convert old format to new format with masks + self.ip_history = [ + {"prefix": ip, "mask": "255.255.255.0"} + for ip in history_data + ] + else: + self.ip_history = history_data else: self.ip_history = [] except Exception as e: @@ -737,31 +787,79 @@ class IPChangerApp: self.log_message(f"Error saving IP history: {e}") def add_to_history(self, ip_prefix: str): - if ip_prefix and ip_prefix not in self.ip_history: - self.ip_history.insert(0, ip_prefix) - self.ip_history = self.ip_history[:15] # Mantener solo las últimas 15 IPs - self.save_ip_history() - self.update_history_display() + if not ip_prefix: + return + + # Get current subnet mask + subnet_mask = self.subnet_mask.get().strip() + + # Look for this prefix in history + existing_index = -1 + for i, entry in enumerate(self.ip_history): + if entry["prefix"] == ip_prefix: + existing_index = i + break + + if existing_index >= 0: + # Found existing entry - update mask and move to top + entry = self.ip_history.pop(existing_index) + entry["mask"] = subnet_mask + self.ip_history.insert(0, entry) + else: + # New entry - add to top + self.ip_history.insert(0, {"prefix": ip_prefix, "mask": subnet_mask}) + + # Keep only the latest 15 entries + self.ip_history = self.ip_history[:15] + + # Save and update display + self.save_ip_history() + self.update_history_display() + + # Save the last used subnet mask + self.config.last_subnet_mask = subnet_mask + self.config.save_config() def update_history_display(self): - self.history_combo["values"] = self.ip_history - if self.ip_history: - self.history_combo.set(self.ip_history[0]) + # Create display values for combo box - show prefix and mask + display_values = [ + f"{entry['prefix']} ({entry['mask']})" for entry in self.ip_history + ] + self.history_combo["values"] = display_values + if display_values: + self.history_combo.set(display_values[0]) def on_history_selected(self, event): - selected_ip = self.history_var.get() - if selected_ip: - # Ensure we only get the first three octets - ip_parts = selected_ip.split(".") - if len(ip_parts) >= 3: - ip_prefix = ".".join(ip_parts[:3]) - self.ip_prefix.set(ip_prefix) - self.config.last_ip_prefix = ip_prefix - self.config.save_config() - self.log_message(f"Selected IP prefix from history: {ip_prefix}") + selected_value = self.history_var.get() + if not selected_value: + return - # Update ping target for the new IP prefix - self.update_ping_target(ip_prefix) + # Parse the prefix from the display string + ip_parts = selected_value.split()[0] # Get first part before space + if "(" in ip_parts: # For safety, handle both formats + ip_parts = ip_parts.split("(")[0].strip() + + # Ensure we only get the first three octets + parts = ip_parts.split(".") + if len(parts) >= 3: + ip_prefix = ".".join(parts[:3]) + self.ip_prefix.set(ip_prefix) + self.config.last_ip_prefix = ip_prefix + + # Find the selected entry in history + for entry in self.ip_history: + if entry["prefix"] == ip_prefix: + # Set the subnet mask from history + self.subnet_mask.set(entry["mask"]) + # Move this entry to top of history (most recently used) + self.add_to_history(ip_prefix) + break + + self.config.save_config() + self.log_message(f"Selected IP prefix from history: {ip_prefix}") + + # Update ping target for the new IP prefix + self.update_ping_target(ip_prefix) def refresh_interfaces(self): self.interfaces.clear() @@ -882,6 +980,10 @@ class IPChangerApp: prefix = ".".join(interface.ip_address.split(".")[:3]) self.ip_prefix.set(prefix) + # Set subnet mask from interface + if interface.subnet_mask: + self.subnet_mask.set(interface.subnet_mask) + # Update ping target for the new IP prefix self.update_ping_target(prefix) @@ -1595,7 +1697,7 @@ class IPChangerApp: ip = result subnet_mask = self.subnet_mask.get() - # Guardar IP actual en el historial + # Guardar IP actual en el historial con la máscara de subred self.add_to_history(self.ip_prefix.get()) # Log the actual command we'll be executing @@ -2016,6 +2118,13 @@ class IPChangerApp: self.log_message(f"Error looking up MAC vendor: {str(e)}") return "" + # Method to handle CIDR preset buttons + def set_cidr_preset(self, cidr_bits): + """Set CIDR to a preset value""" + self.log_message(f"Setting CIDR to preset: /{cidr_bits}") + self.cidr_bits.set(str(cidr_bits)) + # The on_cidr_bits_changed trace will update the subnet mask automatically + def main(): root = tk.Tk() diff --git a/ping_targets.json b/ping_targets.json index 83e3cfe..172fdc2 100644 --- a/ping_targets.json +++ b/ping_targets.json @@ -1 +1 @@ -{"10.1.22": "10.1.22.11", "10.138.182": "10.138.182.94", "10.1.20": "10.1.33.11", "10.255.255": "10.255.255.1", "192.168.88": "192.168.88.1"} \ No newline at end of file +{"10.1.22": "10.1.22.11", "10.138.182": "10.138.182.94", "10.1.20": "10.1.33.11", "10.255.255": "10.255.255.1", "192.168.88": "192.168.88.1", "192.168.212": "192.168.212.212", "192.168.1": "192.168.1.212", "10.1.33": "192.168.88.1", "192.168.193": "192.168.1.212"} \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..2336ce1 --- /dev/null +++ b/readme.md @@ -0,0 +1,59 @@ +# Network Interface IP Configuration Utility + +A Python-based desktop application for managing and configuring network interfaces on Windows systems. + + +## Features + +- **Easy IP Configuration**: Quickly set static IP addresses or enable DHCP +- **IP History**: Remembers previously used IP addresses for quick switching +- **Network Tools**: Built-in ping tool with continuous ping support +- **Network Scanner**: Discover active hosts on your network +- **Host Information**: Lookup hostname and MAC address information +- **MAC Vendor Lookup**: Identify device manufacturers by MAC address + +## Key Functions + +- Change IP address and subnet mask with administrator privileges +- Ping hosts with detailed response information +- Scan IP ranges to discover active hosts +- Save and restore network configurations +- View MAC addresses and vendor information + +## Requirements + +- Windows operating system +- Python 3.6+ +- See requirements.txt for Python dependencies + +## Installation + +1. Clone this repository: + ``` + git clone https://github.com/miguefinghub/IPchangeNG.git + ``` + +2. Install required dependencies: + ``` + pip install -r requirements.txt + ``` + +3. Run the application: + ``` + python menu-ip-change.py + ``` + +Note: Administrative privileges are required to change network settings. + +## Usage + +See the [Wiki](WIKI.md) for detailed usage instructions. + +## License + +[MIT License](LICENSE) + +## Acknowledgements + +- Uses various Python libraries for network operations +- MAC vendor lookup provided by the mac-vendor-lookup package \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index e82aa78..8229ed4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,6 @@ -# Network-related packages -pythonping>=1.1.4 +tkinter>=8.6 +pythonping>=1.1.0 pysnmp>=4.4.12 wmi>=1.5.1 mac-vendor-lookup>=0.1.11 - -# Other dependencies -ipaddress>=1.0.23 +ipaddress>=1.0.23 \ No newline at end of file