658 lines
19 KiB
C
658 lines
19 KiB
C
#define SLIRP_DEPRECATED_OK 1
|
|
/*
|
|
* ipod-netd : réseau pour l'émulateur iPod Touch 2G, sans privilèges.
|
|
*
|
|
* Termine le lien PPP que pppd (côté iPhone OS) ouvre sur l'UART1 émulée, et passe les paquets IP à libslirp :
|
|
* NAT vers Internet, DNS (10.0.2.3), redirections de ports hôte -> iPod.
|
|
*
|
|
* ipod-netd [-s uart1.sock] [-L [addr:]hostport:guestport]... [-v]
|
|
*
|
|
* Adresses : iPod 10.0.2.15, passerelle 10.0.2.2, DNS 10.0.2.3. Défaut : -L 127.0.0.1:7788:7788 (emuagent).
|
|
*/
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include <poll.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <slirp/libslirp.h>
|
|
|
|
#define GUEST_IP "10.0.2.15"
|
|
#define GW_IP "10.0.2.2"
|
|
#define DNS_IP "10.0.2.3"
|
|
|
|
static const uint8_t guest_mac[6] = { 0x52, 0x54, 0x00, 0x12, 0x34, 0x56 };
|
|
static const uint8_t slirp_mac[6] = { 0x52, 0x55, 0x0a, 0x00, 0x02, 0x02 };
|
|
|
|
static int verbose;
|
|
static int uart = -1;
|
|
static const char *sock_path;
|
|
static Slirp *slirp;
|
|
|
|
static void logm(const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
struct timespec ts;
|
|
clock_gettime(CLOCK_REALTIME, &ts);
|
|
fprintf(stderr, "[%ld.%03ld] ", (long)ts.tv_sec % 100000, ts.tv_nsec / 1000000);
|
|
va_start(ap, fmt);
|
|
vfprintf(stderr, fmt, ap);
|
|
va_end(ap);
|
|
fputc('\n', stderr);
|
|
}
|
|
|
|
/* ---------- PPP : trames HDLC (RFC 1662) ---------- */
|
|
|
|
static uint16_t fcstab[256];
|
|
|
|
static void fcs_init(void)
|
|
{
|
|
for (int b = 0; b < 256; b++) {
|
|
uint16_t v = b;
|
|
for (int i = 0; i < 8; i++) {
|
|
v = (v & 1) ? (v >> 1) ^ 0x8408 : v >> 1;
|
|
}
|
|
fcstab[b] = v;
|
|
}
|
|
}
|
|
|
|
static uint16_t fcs16(uint16_t fcs, const uint8_t *p, size_t len)
|
|
{
|
|
while (len--) {
|
|
fcs = (fcs >> 8) ^ fcstab[(fcs ^ *p++) & 0xff];
|
|
}
|
|
return fcs;
|
|
}
|
|
|
|
static void ppp_send(uint16_t proto, const uint8_t *data, size_t len)
|
|
{
|
|
static uint8_t raw[4 + 2048], out[2 * (sizeof(raw) + 2) + 2];
|
|
if (uart < 0 || len > 2048) {
|
|
return;
|
|
}
|
|
raw[0] = 0xff; raw[1] = 0x03; raw[2] = proto >> 8; raw[3] = proto & 0xff;
|
|
memcpy(raw + 4, data, len);
|
|
size_t n = 4 + len;
|
|
uint16_t fcs = fcs16(0xffff, raw, n) ^ 0xffff;
|
|
raw[n++] = fcs & 0xff;
|
|
raw[n++] = fcs >> 8;
|
|
|
|
size_t o = 0;
|
|
out[o++] = 0x7e;
|
|
for (size_t i = 0; i < n; i++) {
|
|
uint8_t c = raw[i];
|
|
if (c < 0x20 || c == 0x7e || c == 0x7d) {
|
|
out[o++] = 0x7d;
|
|
c ^= 0x20;
|
|
}
|
|
out[o++] = c;
|
|
}
|
|
out[o++] = 0x7e;
|
|
for (size_t w = 0; w < o;) {
|
|
ssize_t r = write(uart, out + w, o - w);
|
|
if (r < 0) {
|
|
if (errno == EINTR || errno == EAGAIN) {
|
|
continue;
|
|
}
|
|
return;
|
|
}
|
|
w += r;
|
|
}
|
|
}
|
|
|
|
/* ---------- LCP / IPCP ---------- */
|
|
|
|
enum { CONF_REQ = 1, CONF_ACK, CONF_NAK, CONF_REJ, TERM_REQ, TERM_ACK, CODE_REJ, PROTO_REJ, ECHO_REQ, ECHO_REP, DISC_REQ };
|
|
|
|
#define PROTO_IP 0x0021
|
|
#define PROTO_LCP 0xc021
|
|
#define PROTO_IPCP 0x8021
|
|
|
|
struct ncp {
|
|
const char *name;
|
|
uint16_t proto;
|
|
int acked_peer; /* on a acquitté la requête du pair */
|
|
int acked_ours; /* le pair a acquitté la nôtre */
|
|
uint8_t id;
|
|
time_t last_req;
|
|
};
|
|
|
|
static struct ncp lcp = { "LCP", PROTO_LCP }, ipcp = { "IPCP", PROTO_IPCP };
|
|
static uint32_t lcp_magic = 0x49504f44; /* "IPOD" */
|
|
|
|
static int is_open(struct ncp *n)
|
|
{
|
|
return n->acked_peer && n->acked_ours;
|
|
}
|
|
|
|
static void send_ctl(uint16_t proto, uint8_t code, uint8_t id, const uint8_t *opts, size_t len)
|
|
{
|
|
uint8_t pkt[1500];
|
|
pkt[0] = code; pkt[1] = id; pkt[2] = (len + 4) >> 8; pkt[3] = (len + 4) & 0xff;
|
|
memcpy(pkt + 4, opts, len);
|
|
ppp_send(proto, pkt, len + 4);
|
|
}
|
|
|
|
static void send_conf_req(struct ncp *n)
|
|
{
|
|
uint8_t opts[16];
|
|
size_t len = 0;
|
|
if (n == &ipcp) {
|
|
struct in_addr a;
|
|
inet_aton(GW_IP, &a);
|
|
opts[len++] = 3; opts[len++] = 6;
|
|
memcpy(opts + len, &a, 4); len += 4;
|
|
} else {
|
|
opts[len++] = 5; opts[len++] = 6;
|
|
uint32_t m = htonl(lcp_magic);
|
|
memcpy(opts + len, &m, 4); len += 4;
|
|
}
|
|
send_ctl(n->proto, CONF_REQ, ++n->id, opts, len);
|
|
n->last_req = time(NULL);
|
|
}
|
|
|
|
static void guest_announce(void);
|
|
|
|
static void ncp_opened(struct ncp *n)
|
|
{
|
|
logm("%s ouvert", n->name);
|
|
if (n == &lcp) {
|
|
send_conf_req(&ipcp);
|
|
} else {
|
|
logm("lien IP prêt : iPod %s, passerelle %s, DNS %s", GUEST_IP, GW_IP, DNS_IP);
|
|
guest_announce();
|
|
}
|
|
}
|
|
|
|
/* Traite une Configure-Request du pair : Ack / Nak / Rej, option par option (RFC 1661 §5.1) */
|
|
static void handle_conf_req(struct ncp *n, uint8_t id, const uint8_t *opts, size_t len)
|
|
{
|
|
uint8_t ack[1500], nak[1500], rej[1500];
|
|
size_t na = 0, nn = 0, nr = 0;
|
|
struct in_addr want_ip, want_dns;
|
|
inet_aton(GUEST_IP, &want_ip);
|
|
inet_aton(DNS_IP, &want_dns);
|
|
|
|
for (size_t i = 0; i + 2 <= len;) {
|
|
uint8_t type = opts[i], olen = opts[i + 1];
|
|
if (olen < 2 || i + olen > len) {
|
|
break;
|
|
}
|
|
const uint8_t *o = opts + i;
|
|
int reject = 0, naked = 0;
|
|
if (n == &lcp) {
|
|
switch (type) {
|
|
case 1: /* MRU */
|
|
case 2: /* ACCM : on échappe de toute façon tous les caractères de contrôle */
|
|
case 5: /* magic */
|
|
case 7: /* PFC */
|
|
case 8: /* ACFC */
|
|
break;
|
|
default: /* authentification, callback... */
|
|
reject = 1;
|
|
}
|
|
} else {
|
|
if ((type == 3 || type == 129 || type == 131) && olen == 6) {
|
|
const struct in_addr *want = type == 3 ? &want_ip : &want_dns;
|
|
if (memcmp(o + 2, want, 4)) {
|
|
memcpy(nak + nn, o, 2);
|
|
memcpy(nak + nn + 2, want, 4);
|
|
nn += 6;
|
|
naked = 1;
|
|
}
|
|
} else {
|
|
reject = 1; /* compression VJ, NBNS... */
|
|
}
|
|
}
|
|
if (reject) {
|
|
memcpy(rej + nr, o, olen); nr += olen;
|
|
} else if (!naked) {
|
|
memcpy(ack + na, o, olen); na += olen;
|
|
}
|
|
i += olen;
|
|
}
|
|
if (nr) {
|
|
send_ctl(n->proto, CONF_REJ, id, rej, nr);
|
|
} else if (nn) {
|
|
send_ctl(n->proto, CONF_NAK, id, nak, nn);
|
|
} else {
|
|
int was_open = is_open(n);
|
|
send_ctl(n->proto, CONF_ACK, id, ack, na);
|
|
n->acked_peer = 1;
|
|
if (!n->acked_ours) {
|
|
send_conf_req(n);
|
|
}
|
|
if (!was_open && is_open(n)) {
|
|
ncp_opened(n);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void handle_ctl(struct ncp *n, const uint8_t *p, size_t len)
|
|
{
|
|
if (len < 4) {
|
|
return;
|
|
}
|
|
uint8_t code = p[0], id = p[1];
|
|
size_t plen = (p[2] << 8) | p[3];
|
|
if (plen < 4 || plen > len) {
|
|
return;
|
|
}
|
|
const uint8_t *data = p + 4;
|
|
size_t dlen = plen - 4;
|
|
if (verbose) {
|
|
logm("%s <- code %d id %d len %zu", n->name, code, id, dlen);
|
|
}
|
|
|
|
switch (code) {
|
|
case CONF_REQ:
|
|
if (n == &lcp && is_open(&lcp)) { /* renégociation : tout repart de zéro */
|
|
lcp.acked_ours = 0;
|
|
ipcp.acked_peer = ipcp.acked_ours = 0;
|
|
}
|
|
if (n == &ipcp && !is_open(&lcp)) {
|
|
return;
|
|
}
|
|
handle_conf_req(n, id, data, dlen);
|
|
break;
|
|
case CONF_ACK:
|
|
if (id == n->id && !n->acked_ours) {
|
|
n->acked_ours = 1;
|
|
if (is_open(n)) {
|
|
ncp_opened(n);
|
|
}
|
|
}
|
|
break;
|
|
case CONF_NAK:
|
|
case CONF_REJ:
|
|
/* nos options sont minimales : on les renvoie sans l'option refusée */
|
|
send_ctl(n->proto, CONF_REQ, ++n->id, NULL, 0);
|
|
break;
|
|
case TERM_REQ:
|
|
send_ctl(n->proto, TERM_ACK, id, NULL, 0);
|
|
logm("%s fermé par l'iPod", n->name);
|
|
n->acked_peer = n->acked_ours = 0;
|
|
if (n == &lcp) {
|
|
ipcp.acked_peer = ipcp.acked_ours = 0;
|
|
}
|
|
break;
|
|
case ECHO_REQ:
|
|
if (n == &lcp && dlen >= 4) {
|
|
uint8_t rep[1500];
|
|
uint32_t m = htonl(lcp_magic);
|
|
memcpy(rep, &m, 4);
|
|
memcpy(rep + 4, data + 4, dlen - 4);
|
|
send_ctl(PROTO_LCP, ECHO_REP, id, rep, dlen);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* ---------- IP <-> slirp (trames Ethernet fictives) ---------- */
|
|
|
|
static void log_ip(const char *dir, const uint8_t *ip, size_t len)
|
|
{
|
|
if (!verbose || len < 20) return;
|
|
char a[16], b[16];
|
|
snprintf(a, sizeof a, "%d.%d.%d.%d", ip[12], ip[13], ip[14], ip[15]);
|
|
snprintf(b, sizeof b, "%d.%d.%d.%d", ip[16], ip[17], ip[18], ip[19]);
|
|
int ihl = (ip[0] & 0xf) * 4, proto = ip[9];
|
|
if (proto == 1 && len > (size_t)ihl + 1) logm("%s ICMP %s -> %s type %d code %d", dir, a, b, ip[ihl], ip[ihl + 1]);
|
|
else if ((proto == 6 || proto == 17) && len >= (size_t)ihl + 4)
|
|
logm("%s %s %s:%d -> %s:%d len %zu", dir, proto == 6 ? "TCP" : "UDP", a, (ip[ihl] << 8) | ip[ihl + 1], b, (ip[ihl + 2] << 8) | ip[ihl + 3], len);
|
|
else logm("%s proto %d %s -> %s", dir, proto, a, b);
|
|
}
|
|
|
|
static void slirp_ip_input(const uint8_t *ip, size_t len)
|
|
{
|
|
log_ip("iPod ->", ip, len);
|
|
uint8_t frame[14 + 2048];
|
|
if (len > 2048) {
|
|
return;
|
|
}
|
|
memcpy(frame, slirp_mac, 6);
|
|
memcpy(frame + 6, guest_mac, 6);
|
|
frame[12] = 0x08; frame[13] = 0x00;
|
|
memcpy(frame + 14, ip, len);
|
|
slirp_input(slirp, frame, len + 14);
|
|
}
|
|
|
|
static void arp_packet(uint16_t op, const uint8_t *dst_mac, const uint8_t *target_ip)
|
|
{
|
|
uint8_t f[42];
|
|
struct in_addr gip;
|
|
inet_aton(GUEST_IP, &gip);
|
|
memcpy(f, dst_mac, 6);
|
|
memcpy(f + 6, guest_mac, 6);
|
|
f[12] = 0x08; f[13] = 0x06;
|
|
f[14] = 0; f[15] = 1; f[16] = 8; f[17] = 0; f[18] = 6; f[19] = 4;
|
|
f[20] = 0; f[21] = op;
|
|
memcpy(f + 22, guest_mac, 6);
|
|
memcpy(f + 28, &gip, 4);
|
|
memcpy(f + 32, op == 2 ? dst_mac : (const uint8_t *)"\0\0\0\0\0\0", 6);
|
|
memcpy(f + 38, target_ip, 4);
|
|
slirp_input(slirp, f, sizeof f);
|
|
}
|
|
|
|
/* ARP gratuit : slirp apprend l'adresse MAC de l'iPod */
|
|
static void guest_announce(void)
|
|
{
|
|
static const uint8_t bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
|
struct in_addr gw;
|
|
inet_aton(GW_IP, &gw);
|
|
arp_packet(1, bcast, (const uint8_t *)&gw);
|
|
}
|
|
|
|
static slirp_ssize_t slirp_send(const void *buf, size_t len, void *opaque)
|
|
{
|
|
const uint8_t *f = buf;
|
|
if (len < 14) {
|
|
return len;
|
|
}
|
|
uint16_t type = (f[12] << 8) | f[13];
|
|
if (type == 0x0806 && len >= 42 && f[21] == 1) {
|
|
struct in_addr gip;
|
|
inet_aton(GUEST_IP, &gip);
|
|
if (!memcmp(f + 38, &gip, 4)) {
|
|
arp_packet(2, f + 22, f + 28);
|
|
}
|
|
} else if (type == 0x0800 && is_open(&ipcp)) {
|
|
log_ip("-> iPod", f + 14, len - 14);
|
|
ppp_send(PROTO_IP, f + 14, len - 14);
|
|
}
|
|
return len;
|
|
}
|
|
|
|
/* ---------- réception PPP ---------- */
|
|
|
|
static void ppp_frame(uint8_t *p, size_t len)
|
|
{
|
|
if (len < 4 || fcs16(0xffff, p, len) != 0xf0b8) {
|
|
if (verbose && len) {
|
|
logm("trame invalide (%zu octets)", len);
|
|
}
|
|
return;
|
|
}
|
|
len -= 2;
|
|
if (len >= 2 && p[0] == 0xff && p[1] == 0x03) {
|
|
p += 2; len -= 2;
|
|
}
|
|
if (!len) {
|
|
return;
|
|
}
|
|
uint16_t proto;
|
|
if (p[0] & 1) {
|
|
proto = p[0]; p += 1; len -= 1;
|
|
} else {
|
|
if (len < 2) {
|
|
return;
|
|
}
|
|
proto = (p[0] << 8) | p[1]; p += 2; len -= 2;
|
|
}
|
|
switch (proto) {
|
|
case PROTO_LCP:
|
|
handle_ctl(&lcp, p, len);
|
|
break;
|
|
case PROTO_IPCP:
|
|
if (is_open(&lcp)) {
|
|
handle_ctl(&ipcp, p, len);
|
|
}
|
|
break;
|
|
case PROTO_IP:
|
|
if (is_open(&ipcp)) {
|
|
slirp_ip_input(p, len);
|
|
}
|
|
break;
|
|
default:
|
|
if (is_open(&lcp)) { /* IPV6CP, CCP... : Protocol-Reject */
|
|
uint8_t rej[1500];
|
|
size_t n = len > 1400 ? 1400 : len;
|
|
rej[0] = proto >> 8; rej[1] = proto & 0xff;
|
|
memcpy(rej + 2, p, n);
|
|
send_ctl(PROTO_LCP, PROTO_REJ, ++lcp.id, rej, n + 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void uart_input(const uint8_t *buf, size_t n)
|
|
{
|
|
static uint8_t frame[4096];
|
|
static size_t flen;
|
|
static int esc;
|
|
for (size_t i = 0; i < n; i++) {
|
|
uint8_t c = buf[i];
|
|
if (c == 0x7e) {
|
|
ppp_frame(frame, flen);
|
|
flen = 0; esc = 0;
|
|
} else if (c == 0x7d) {
|
|
esc = 1;
|
|
} else {
|
|
if (esc) {
|
|
c ^= 0x20; esc = 0;
|
|
}
|
|
if (flen < sizeof frame) {
|
|
frame[flen++] = c;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ---------- boucle principale, timers slirp ---------- */
|
|
|
|
struct timer { SlirpTimerCb cb; void *cb_opaque; SlirpTimerId id; int by_id; int64_t expire; int used; };
|
|
static struct timer timers[16];
|
|
|
|
static int64_t now_ns(void)
|
|
{
|
|
struct timespec ts;
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
return ts.tv_sec * 1000000000LL + ts.tv_nsec;
|
|
}
|
|
|
|
static int64_t cb_clock(void *o) { return now_ns(); }
|
|
static void cb_guest_error(const char *msg, void *o) { logm("slirp : %s", msg); }
|
|
static void cb_notify(void *o) {}
|
|
static void cb_register_poll_fd(int fd, void *o) {} /* on repasse tous les sockets à poll() à chaque tour */
|
|
static void cb_unregister_poll_fd(int fd, void *o) {}
|
|
|
|
static struct timer *timer_alloc(void)
|
|
{
|
|
for (int i = 0; i < 16; i++) {
|
|
if (!timers[i].used) {
|
|
memset(&timers[i], 0, sizeof timers[i]);
|
|
timers[i].used = 1;
|
|
timers[i].expire = -1;
|
|
return &timers[i];
|
|
}
|
|
}
|
|
abort();
|
|
}
|
|
|
|
static void *cb_timer_new(SlirpTimerCb cb, void *cb_opaque, void *o)
|
|
{
|
|
struct timer *t = timer_alloc();
|
|
t->cb = cb; t->cb_opaque = cb_opaque;
|
|
return t;
|
|
}
|
|
|
|
static void *cb_timer_new_opaque(SlirpTimerId id, void *cb_opaque, void *o)
|
|
{
|
|
struct timer *t = timer_alloc();
|
|
t->id = id; t->by_id = 1; t->cb_opaque = cb_opaque;
|
|
return t;
|
|
}
|
|
|
|
static void cb_timer_free(void *t, void *o) { ((struct timer *)t)->used = 0; }
|
|
static void cb_timer_mod(void *t, int64_t expire_ms, void *o) { ((struct timer *)t)->expire = expire_ms; }
|
|
|
|
static struct pollfd pfds[1024];
|
|
static int npfds;
|
|
|
|
static int add_poll(slirp_os_socket fd, int events, void *o)
|
|
{
|
|
short ev = 0;
|
|
if (events & SLIRP_POLL_IN) ev |= POLLIN;
|
|
if (events & SLIRP_POLL_OUT) ev |= POLLOUT;
|
|
if (events & SLIRP_POLL_PRI) ev |= POLLPRI;
|
|
pfds[npfds].fd = fd;
|
|
pfds[npfds].events = ev;
|
|
pfds[npfds].revents = 0;
|
|
return npfds++;
|
|
}
|
|
|
|
static int get_revents(int idx, void *o)
|
|
{
|
|
short r = pfds[idx].revents;
|
|
int ev = 0;
|
|
if (r & POLLIN) ev |= SLIRP_POLL_IN;
|
|
if (r & POLLOUT) ev |= SLIRP_POLL_OUT;
|
|
if (r & POLLPRI) ev |= SLIRP_POLL_PRI;
|
|
if (r & POLLERR) ev |= SLIRP_POLL_ERR;
|
|
if (r & POLLHUP) ev |= SLIRP_POLL_HUP;
|
|
return ev;
|
|
}
|
|
|
|
static int uart_connect(void)
|
|
{
|
|
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
struct sockaddr_un a = { .sun_family = AF_UNIX };
|
|
strncpy(a.sun_path, sock_path, sizeof a.sun_path - 1);
|
|
if (connect(fd, (struct sockaddr *)&a, sizeof a) < 0) {
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
static void add_fwd(const char *spec)
|
|
{
|
|
char buf[128], *parts[3];
|
|
int n = 0;
|
|
strncpy(buf, spec, sizeof buf - 1);
|
|
buf[sizeof buf - 1] = 0;
|
|
for (char *t = strtok(buf, ":"); t && n < 3; t = strtok(NULL, ":")) {
|
|
parts[n++] = t;
|
|
}
|
|
struct in_addr host, guest;
|
|
inet_aton("127.0.0.1", &host);
|
|
inet_aton(GUEST_IP, &guest);
|
|
int hp, gp;
|
|
if (n == 3) {
|
|
inet_aton(parts[0], &host);
|
|
hp = atoi(parts[1]); gp = atoi(parts[2]);
|
|
} else if (n == 2) {
|
|
hp = atoi(parts[0]); gp = atoi(parts[1]);
|
|
} else {
|
|
fprintf(stderr, "redirection invalide : %s\n", spec);
|
|
exit(1);
|
|
}
|
|
if (slirp_add_hostfwd(slirp, 0, host, hp, guest, gp) < 0) {
|
|
logm("redirection %s impossible (port occupé ?)", spec);
|
|
/* Ne pas laisser le client contacter le relais d'une autre session. */
|
|
exit(1);
|
|
} else {
|
|
logm("redirection tcp %s:%d -> iPod:%d", inet_ntoa(host), hp, gp);
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
const char *fwds[32];
|
|
int nfwd = 0;
|
|
static char default_sock[512];
|
|
snprintf(default_sock, sizeof default_sock, "%s/ipod-emu/run/uart1.sock", getenv("HOME"));
|
|
sock_path = default_sock;
|
|
|
|
for (int i = 1; i < argc; i++) {
|
|
if (!strcmp(argv[i], "-s") && i + 1 < argc) sock_path = argv[++i];
|
|
else if (!strcmp(argv[i], "-L") && i + 1 < argc && nfwd < 32) fwds[nfwd++] = argv[++i];
|
|
else if (!strcmp(argv[i], "-v")) verbose = 1;
|
|
else { fprintf(stderr, "usage: %s [-s uart1.sock] [-L [addr:]hostport:guestport]... [-v]\n", argv[0]); return 1; }
|
|
}
|
|
if (!nfwd) fwds[nfwd++] = "127.0.0.1:7788:7788";
|
|
signal(SIGPIPE, SIG_IGN);
|
|
fcs_init();
|
|
|
|
SlirpConfig cfg = { 0 };
|
|
cfg.version = 4;
|
|
cfg.in_enabled = true;
|
|
inet_aton("10.0.2.0", &cfg.vnetwork);
|
|
inet_aton("255.255.255.0", &cfg.vnetmask);
|
|
inet_aton(GW_IP, &cfg.vhost);
|
|
inet_aton(DNS_IP, &cfg.vnameserver);
|
|
inet_aton(GUEST_IP, &cfg.vdhcp_start);
|
|
cfg.vhostname = "ipod-emu-host";
|
|
cfg.if_mtu = 1500;
|
|
cfg.if_mru = 1500;
|
|
static const SlirpCb cb = {
|
|
.send_packet = slirp_send, .guest_error = cb_guest_error, .clock_get_ns = cb_clock,
|
|
.timer_new = cb_timer_new, .timer_free = cb_timer_free, .timer_mod = cb_timer_mod,
|
|
.notify = cb_notify, .timer_new_opaque = cb_timer_new_opaque,
|
|
.register_poll_fd = cb_register_poll_fd, .unregister_poll_fd = cb_unregister_poll_fd,
|
|
};
|
|
slirp = slirp_new(&cfg, &cb, NULL);
|
|
for (int i = 0; i < nfwd; i++) add_fwd(fwds[i]);
|
|
|
|
for (;;) {
|
|
if (uart < 0) {
|
|
uart = uart_connect();
|
|
if (uart < 0) {
|
|
sleep(1);
|
|
continue;
|
|
}
|
|
logm("connecté à %s, en attente de pppd", sock_path);
|
|
lcp.acked_peer = lcp.acked_ours = ipcp.acked_peer = ipcp.acked_ours = 0;
|
|
}
|
|
|
|
uint32_t timeout = 1000;
|
|
npfds = 0;
|
|
int uidx = add_poll(uart, SLIRP_POLL_IN, NULL);
|
|
slirp_pollfds_fill_socket(slirp, &timeout, add_poll, NULL);
|
|
int64_t now_ms = now_ns() / 1000000;
|
|
for (int i = 0; i < 16; i++) {
|
|
if (timers[i].used && timers[i].expire >= 0) {
|
|
int64_t d = timers[i].expire - now_ms;
|
|
if (d < 0) d = 0;
|
|
if (d < timeout) timeout = d;
|
|
}
|
|
}
|
|
int r = poll(pfds, npfds, timeout);
|
|
slirp_pollfds_poll(slirp, r < 0, get_revents, NULL);
|
|
|
|
now_ms = now_ns() / 1000000;
|
|
for (int i = 0; i < 16; i++) {
|
|
struct timer *t = &timers[i];
|
|
if (t->used && t->expire >= 0 && t->expire <= now_ms) {
|
|
t->expire = -1;
|
|
if (t->by_id) slirp_handle_timer(slirp, t->id, t->cb_opaque);
|
|
else t->cb(t->cb_opaque);
|
|
}
|
|
}
|
|
|
|
if (r > 0 && (pfds[uidx].revents & (POLLIN | POLLHUP | POLLERR))) {
|
|
uint8_t buf[4096];
|
|
ssize_t n = read(uart, buf, sizeof buf);
|
|
if (n <= 0) {
|
|
logm("UART déconnectée (émulateur arrêté ?)");
|
|
close(uart);
|
|
uart = -1;
|
|
continue;
|
|
}
|
|
uart_input(buf, n);
|
|
}
|
|
|
|
/* relance des Configure-Request non acquittées */
|
|
time_t t = time(NULL);
|
|
if (is_open(&lcp) && !ipcp.acked_ours && t - ipcp.last_req >= 2) send_conf_req(&ipcp);
|
|
if (lcp.acked_peer && !lcp.acked_ours && t - lcp.last_req >= 2) send_conf_req(&lcp);
|
|
}
|
|
}
|