Skip to content

Commit

Permalink
feat: add link command utils
Browse files Browse the repository at this point in the history
  • Loading branch information
shahradelahi committed Apr 21, 2024
1 parent d518fa9 commit 9c39d54
Show file tree
Hide file tree
Showing 2 changed files with 184 additions and 0 deletions.
33 changes: 33 additions & 0 deletions src/ip/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,31 @@
import { del as linkDel } from './link';

// ------------------------

export * as route from './route';
export * as link from './link';

// ------------------------

/**
* Drop Device
*
* Example:
* ```javascript
* await dropDevice('tun0');
* ```
*
* @param name "DEVICE_NAME" or "dev NAME" or "group NAME"
*/
export async function dropDevice(name: string) {
const { error } = await linkDel({
name,
});

if (error) {
throw error;
}
}

// ------------------------

Expand All @@ -7,6 +34,8 @@ export type IPFamily = 'IPv4' | 'IPv6';
export const IPV4_REGEX = /^(\d{1,3}\.){3}\d{1,3}$/;
export const IPV6_REGEX = /^(::)?(((\d{1,3}\.){3}(\d{1,3}){1})?([0-9a-f]){0,4}:{0,2}){1,8}(::)?$/i;

export const MAC_REGEX = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/;

export function isValidIP(ip: string) {
return isValidIPv4(ip) || isValidIPv6(ip);
}
Expand Down Expand Up @@ -150,3 +179,7 @@ export function* generateIP(cidr: string): Generator<string, void> {
yield fromLong(currentIP + i);
}
}

export function isValidMacAddress(mac: string) {
return MAC_REGEX.test(mac);
}
151 changes: 151 additions & 0 deletions src/ip/link.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { execShell } from '@/utils/exec-shell';
import { isValidMacAddress } from '@/ip/index';

export const LINK_TYPES = <const>[
'amt',
'bareudp',
'bond',
'bond_slave',
'bridge',
'bridge_slave',
'dsa',
'dummy',
'erspan',
'geneve',
'gre',
'gretap',
'gtp',
'ifb',
'ip6erspan',
'ip6gre',
'ip6gretap',
'ip6tnl',
'ipip',
'ipoib',
];

export type LinkType = (typeof LINK_TYPES)[number];

export type AddParams = Record<string, string> & {
type?: LinkType;
name?: string;
txqueuelen?: number;
address?: string;
broadcast?: string;
mtu?: number;
numtxqueues?: number;
numrxqueues?: number;
netns?: string;
};

export async function add(params: AddParams) {
const args = [
'ip',
'link',
'add',
...Object.entries(params).map(([key, value]) => `${key.toLowerCase()} ${value}`),
];
return execShell(args);
}

export type DelParams = Record<string, string> & {
name?: string;
type?: LinkType;
};

export async function del(params: DelParams) {
const args = [
'ip',
'link',
'delete',
...Object.entries(params).map(([key, value]) => `${key.toLowerCase()} ${value}`),
];
return execShell(args);
}

export type ListParams = Record<string, string> & {
name?: string;
type?: LinkType;
};

export type Link = {
name: string;
flags: string[];
qdisc: string;
state: string;
mode: string;
group: string;
qlen?: number;
mtu: number;
address?: string;
broadcast?: string;
};

export async function list(params: ListParams = {}): Promise<Link[]> {
const args = [
'ip',
'link',
'show',
...Object.entries(params).map(([key, value]) => `${key.toLowerCase()} ${value}`),
];

const { error, data } = await execShell(args);

if (error) {
throw error;
}

const lines = data.output.match(/^\d+:(.*|(?:\n\s)+)+/gm)!;
const links: Link[] = [];

for (const line of lines) {
const link = parseLink(line);
if (link) {
links.push(link);
}
}

return links;
}

export function parseLink(line: string) {
if (!line) {
return undefined;
}

const parts = line.replace(/\n$/, ' ').split(/\s+/).slice(1);
if (parts.length < 6) {
return undefined;
}

const link: Link = {
name: parts[0].slice(0, -1),
flags: parts[1].slice(1, -1).split(','),
qdisc: '',
state: '',
mode: '',
group: '',
qlen: undefined,
mtu: -1,
address: undefined,
broadcast: undefined,
};

for (const part of parts.slice(2)) {
const next = parts[parts.indexOf(part) + 1];
if (part in link) {
// @ts-expect-error Type 'string' is not assignable to type 'Link[keyof Link]'.
link[part] = next.match(/^\d+$/) ? +next : next;
}

if (part.startsWith('link/') && isValidMacAddress(next)) {
link.address = next;
}

if (part === 'brd') {
link.broadcast = next;
}
}

return link;
}

0 comments on commit 9c39d54

Please sign in to comment.