UFiber.Configurator/src/UFiber.Configurator/Program.cs

201 lines
7.0 KiB
C#

using System;
using System.IO;
using Renci.SshNet;
using UFiber.Configurator;
using System.CommandLine;
using System.CommandLine.Invocation;
var rootCommand = new RootCommand("Apply configuration changes to UFiber devices")
{
new Option<string>(
"--host",
getDefaultValue: () => "192.168.1.1",
"IP or hostname of the target UFiber device."),
new Option<string>(
"--user",
getDefaultValue: () => "ubnt",
"SSH user name."),
new Option<string>(
"--pw",
getDefaultValue: () => "ubnt",
"SSH password."),
new Option<int>(
"--port",
getDefaultValue: () => 22,
"SSH port of the target UFiber device."),
new Option<bool>(
"--dry-run",
"Don't apply the patched file to the target UFiber device. (i.e. dry-run)"),
new Option<string>(
"--slid",
"The SLID (or PLOAM Password).", ArgumentArity.ZeroOrOne),
new Option<string>(
"--vendor",
"4-digit Vendor Id (e.g. HWTC, MTSC, etc.). Combined with --serial, a GPON Serial Number is built.", ArgumentArity.ZeroOrOne),
new Option<string>(
"--serial",
"8-digit (e.g. 01234567) serial number or 16-digit (e.g. 41-4C-43-4C-xx-xx-xx-xx) HEX serial number. Combined with --vendor, a GPON Serial Number is built. Note: If a 16-digit HEX value is provided, the first 4 bytes (8 digits) will replace whatever value was passed to Vendor Id with '--vendor'.", ArgumentArity.ZeroOrOne),
new Option<string>(
"--mac",
"The desired MAC address to clone.", ArgumentArity.ZeroOrOne),
};
SshClient GetSSHClient(string userName, string password, string host, int port = 22)
{
var client = new SshClient(host, port, userName, password);
client.Connect();
return client;
}
ScpClient GetSCPClient(string userName, string password, string host, int port = 22)
{
var client = new ScpClient(host, port, userName, password);
client.Connect();
return client;
}
rootCommand.Handler = CommandHandler
.Create<string, string, string, int, bool, string, string, string, string>(
(host, user, pw, port, dryRun, slid, vendor, serial, mac) =>
{
if (string.IsNullOrWhiteSpace(host))
{
Console.Error.WriteLine("Host is a required parameter and can't be empty.");
Environment.ExitCode = -1;
return;
}
SshClient ssh = default!;
ScpClient scp = default!;
try
{
// Connect to SSH and SCP
ssh = GetSSHClient(user, pw, host, port);
scp = GetSCPClient(user, pw, host, port);
}
catch (Exception ex)
{
Console.Error.WriteLine($"Unable to connect to the target UFiber device. Please check the connection parameters and try again. Error: {ex.Message}");
Environment.ExitCode = -1;
return;
}
var imgName = $"fw-{DateTime.UtcNow.ToString("ddMMyyyy-hhmmss")}.bin";
// Dump the image file
var cmd = ssh.RunCommand($"cat /dev/mtdblock3 > /tmp/{imgName}");
if (cmd.ExitStatus != 0)
{
Console.Error.WriteLine($"Failute to dump the image file. Error: {cmd.Error}");
Environment.ExitCode = cmd.ExitStatus;
return;
}
const string localDumps = "./dumps";
if (!Directory.Exists(localDumps))
{
Directory.CreateDirectory(localDumps);
}
// Download the dump
try
{
scp.Download($"/tmp/{imgName}", new DirectoryInfo(localDumps));
ssh.RunCommand($"rm /tmp/{imgName}");
}
catch (Exception ex)
{
Console.Error.WriteLine($"Failure downloading original image file from the UFiber device. Error: {ex.Message}.");
Environment.ExitCode = -1;
return;
}
var ram = new NVRAM(File.ReadAllBytes(Path.Combine(localDumps, imgName)));
Console.WriteLine("### Original Image ###");
Console.WriteLine(ram);
Console.WriteLine($"### Patching {imgName}...");
if ((string.IsNullOrWhiteSpace(vendor) && !string.IsNullOrWhiteSpace(serial)) ||
(!string.IsNullOrWhiteSpace(vendor) && string.IsNullOrWhiteSpace(serial)))
{
Console.Error.WriteLine($"To set the GPON Serial Number, you must pass both --vendor and --serial");
Environment.ExitCode = -1;
return;
}
else if (!string.IsNullOrWhiteSpace(vendor) && !string.IsNullOrWhiteSpace(serial))
{
ram.SetGponId(vendor);
ram.SetGponSerialNumber(serial);
}
if (!string.IsNullOrWhiteSpace(mac))
{
ram.SetBaseMacAddress(mac);
}
if (!string.IsNullOrWhiteSpace(slid))
{
ram.SetGponPassword(slid);
}
var patched = ram.CompletePatch();
const string localPatched = "./patched";
if (!Directory.Exists(localPatched))
{
Directory.CreateDirectory(localPatched);
}
var patchedFileName = $"patched-{imgName}";
File.WriteAllBytes(Path.Combine(localPatched, patchedFileName), patched);
Console.WriteLine($"### Patched {imgName}!");
Console.WriteLine(ram);
if (!dryRun)
{
Console.WriteLine("Uploading patched file to the target UFiber device...");
try
{
scp.Upload(new FileInfo(Path.Combine(localPatched, patchedFileName)), $"/tmp/{patchedFileName}");
}
catch (Exception ex)
{
Console.Error.WriteLine($"Failure uploading patched image file to the UFiber device. Error: {ex.Message}.");
Environment.ExitCode = -1;
return;
}
Console.WriteLine("Uploaded!");
Console.WriteLine("### Applying patched file on the target UFiber device...");
cmd = ssh.RunCommand($"dd if=/tmp/{patchedFileName} of=/dev/mtdblock3 && rm /tmp/{patchedFileName}");
if (cmd.ExitStatus != 0)
{
Console.Error.WriteLine($"Failure to apply patched image file. Error: {cmd.Error}");
Environment.ExitCode = cmd.ExitStatus;
return;
}
Console.WriteLine("### Applied patch! Please reboot your UFiber device to load the new image.");
}
else
{
Console.WriteLine($"### Dry-run completed. The patched file can be found at '{Path.Combine(localDumps, patchedFileName)}'.");
}
});
return rootCommand.Invoke(args);