Browse Source

reghack and other tools in one place

Danja Vasiliev 10 years ago
parent
commit
8f90d4d4be

+ 327 - 0
openwrt/tools/dlink-flash/dlink-flash.c

@@ -0,0 +1,327 @@
+/* dlink-flash - Flash D-Link DIR505 (and potentially other D-Links) 
+ * using recovery web flashing without using Internet Explorer 8
+ *
+ * SuperGlue project | http://superglue.it
+ * Danja Vasiliev <danja@k0a1a.net> | 2014
+ * Based on original work by Daniel Dickinson, 2014
+ *
+ * - Compile this tool:
+ *
+ *   $ gcc -o dlink-flash dlink-flash.c
+ * 
+ * - Boot your DIR-505 unit while holding Reset until red LED begins to blink slowly
+ * - Configure your host network interface:
+ *
+ *   # ifconfig eth0 192.168.0.2 netmask 255.255.255.0 up
+ *
+ * - Upload desired (original) firmware:
+ *
+ *   $ ./dlink-flash firmware.bin 
+ *
+ * - Allow a few minutes for flashing (no indication provided)
+ * - Power cycle the unit to boot to new firmware
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>. 
+ */
+ 
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <regex.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <errno.h>
+ 
+void build_post_bin(uint8_t **post, size_t *post_len, uint8_t *newdata, size_t datalen) {
+    uint8_t *newpost = NULL;
+ 
+    newpost = malloc((*post_len + datalen) * sizeof(uint8_t));
+    if (*post) {
+  memcpy(newpost, *post, *post_len);
+    } else {
+        *post_len = 0;
+    }
+    memcpy(newpost + *post_len, newdata, datalen);
+    *post_len += datalen;    
+    if (*post)
+  free(*post);    
+    *post = newpost;
+}
+ 
+void build_post(uint8_t **post, size_t *post_len, char *newchar, size_t *content_len) {
+    uint8_t *newpost = NULL;
+    size_t nlen;
+    build_post_bin(post, post_len, newchar, strlen(newchar));
+    if (content_len) {
+        *content_len += strlen(newchar);
+    }
+}
+ 
+void usage(char *exename) {
+    printf("Usage: %s <filename> [-d]\n", exename);
+    printf("   Interface attached to D-Link must have IP addres 192.168.0.2");
+    exit(1);
+}
+ 
+int open_socket(void) {
+    /* we need TCP window to be 1024 bytes long */
+    int sock = socket(AF_INET, SOCK_STREAM, 0);
+    unsigned int tcpflush = 1;
+    unsigned int recvbufsz = 1024;
+    unsigned int smallwindow = 1024;
+    unsigned int mss = 2048;
+ 
+    setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &recvbufsz, sizeof(recvbufsz));
+    setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &recvbufsz, sizeof(recvbufsz));
+    setsockopt(sock, IPPROTO_IP, TCP_NODELAY, &tcpflush, sizeof(tcpflush));
+    setsockopt(sock, IPPROTO_IP, TCP_MAXSEG, &mss, sizeof(mss));
+    setsockopt(sock, IPPROTO_IP, TCP_WINDOW_CLAMP, &smallwindow, sizeof(smallwindow));
+ 
+    struct sockaddr_in ipaddr;
+    /* D-Link default recovery IP */
+    in_addr_t hostip = inet_addr("192.168.0.1");
+    ipaddr.sin_family = AF_INET;
+    ipaddr.sin_port = htons(80);
+    ipaddr.sin_addr.s_addr = hostip;
+    if (connect(sock, (struct sockaddr *)&ipaddr, sizeof(struct sockaddr)) < 0) {
+        return -1;
+    }
+    return sock;
+}
+ 
+void send_get(int *sock, uint8_t *get, size_t getlen, uint8_t *post, int debug) {
+    size_t socksent = 0;
+    size_t curpos = 0;
+    *sock = open_socket();
+    if (*sock < 0) {
+        perror("send_get");
+        free(get);
+        if (post)
+            free(post);
+        exit(7);
+    }
+    while (curpos < getlen) {
+        if ((getlen - curpos) >= 1024) {
+      socksent = send(*sock, get + curpos, 1024, 0);
+            if (debug)
+                fprintf(stderr, "Sent %d bytes\n", socksent);
+      if (socksent < 0) {
+          perror("send_get");
+    close(*sock);
+    free(get);
+                if (post)
+        free(post);
+    exit(7);
+            }
+  } else {
+      socksent = send(*sock, get + curpos, getlen - curpos, 0);
+            if (debug)
+                fprintf(stderr, "Sent %d bytes\n", socksent);
+      if (socksent < 0) {
+          perror("send_get");
+    close(*sock);
+    free(get);
+                if (post)
+        free(post);
+    exit(7);
+            }
+        }
+        curpos += socksent;
+        printf("\r%d/%d Bytes written: GET %g%% complete                          ", curpos, getlen, ((float)curpos / (float)getlen) * (float)100);
+  fflush(stdout);
+    }
+    printf("\nFinished sending GET. Waiting for response.\n");
+}
+ 
+int main(int argc, char *argv[]) {
+    uint8_t *firmware = NULL;   
+    uint8_t *post = NULL;
+    size_t postlen = 0;
+    uint8_t *get = NULL;
+    size_t getlen = 0;
+    uint8_t *content = NULL;
+    size_t contentlen = 0;
+    size_t nonnllen = 0;
+    size_t firmwarelen = 0;
+    char contentlenstr[2048];
+    size_t curpos = 0;
+    contentlenstr[0] = 0;
+    int debug = 0;
+ 
+    if (argc < 2) {
+  usage(argv[0]);
+    }
+ 
+   if (argc == 3) {
+        if (!strncmp(argv[2], "-d", 2)) {
+      debug = 1;
+        } else {
+      usage(argv[0]);
+        }
+    } else if (argc > 2) {
+  usage(argv[0]);
+    }
+ 
+    printf("Load firmware file %s\n", argv[1]);
+ 
+    int firmwarefd = open(argv[1], 0);
+    if (firmwarefd < 0) {
+  perror(argv[1]);
+  exit(1);
+    }
+ 
+    size_t len = 0;
+    uint8_t buf[2048];
+    uint8_t *newfw = NULL;
+    int sock;
+ 
+    do {
+        len = read(firmwarefd, &buf[0], 2048);
+        if (len < 0) {
+  perror(argv[1]);
+  close(firmwarefd);
+        if (firmware)
+      free(firmware);
+            exit(2);
+  }
+        if (len > 0) {
+      newfw = malloc((firmwarelen + len) * sizeof(uint8_t));
+      if (firmware)
+    memcpy(newfw, firmware, firmwarelen);
+      memcpy(newfw + firmwarelen, &buf[0], len);
+      firmwarelen += len;
+      if (firmware)
+    free(firmware);
+      firmware = newfw;
+        }
+    } while (len > 0);
+    close(firmwarefd);
+    printf("Firmware %u bytes long\n", firmwarelen);
+ 
+    build_post(&content, &contentlen, "---------------------------7de1fe13304\r\n", NULL);    
+    nonnllen += 2;
+    /* just going to keep this */
+    build_post(&content, &contentlen, "Content-Disposition: form-data; name=\"files\"; filename=\"C:\\My Documents\\firmware.bin\"\r\n", &nonnllen);
+    build_post(&content, &contentlen, "Content-Type: application/octet-stream\r\n", &nonnllen);
+    build_post(&content, &contentlen, "\r\n", &nonnllen);
+    build_post_bin(&content, &contentlen, firmware, firmwarelen);
+    build_post(&content, &contentlen, "\r\n---------------------------7de1fe13304--\r\n", NULL);
+    nonnllen += 4;
+ 
+    sprintf(contentlenstr, "%d\r\n", nonnllen + firmwarelen);
+ 
+    build_post(&post, &postlen, "POST /cgi/index HTTP/1.1\r\n", NULL);
+    build_post(&post, &postlen, "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\r\n", NULL);
+    build_post(&post, &postlen, "Referer: http://192.168.0.1\r\n", NULL);
+    build_post(&post, &postlen, "Accept-Language: en-US\r\n", NULL);
+    build_post(&post, &postlen, "User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)\r\n", NULL);
+    build_post(&post, &postlen, "Content-Type: multipart/form-data; boundary=---------------------------7de1fe13304\r\n", NULL);
+    build_post(&post, &postlen, "Accept-Encoding: gzip, deflate\r\n", NULL);
+    build_post(&post, &postlen, "Host: 192.168.0.1\r\n", NULL);
+    build_post(&post, &postlen, "Content-Length: ", NULL);
+    build_post(&post, &postlen, contentlenstr, NULL);
+    build_post(&post, &postlen, "Connection: Keep-Alive\r\n", NULL);
+    build_post(&post, &postlen, "Cache-Control: no-cache\r\n", NULL);
+    build_post(&post, &postlen, "\r\n", NULL);
+    build_post_bin(&post, &postlen, content, contentlen);
+    free(content);
+    free(firmware);
+ 
+    int gotlen = 0;
+    char recvbuf[2048];
+    int recvlen = recv(sock, &recvbuf[0], 1024, MSG_WAITALL);
+    int newrecvlen;
+    printf("Initiating transfer....");
+    fflush(stdout);
+    sock = open_socket();
+    if (sock < 0) {
+        perror(argv[1]);
+        free(post);
+    }
+    size_t socksent = 0;
+    curpos = 0;
+    while (curpos < postlen) {
+        if ((postlen - curpos) >= 1024) {
+      socksent = send(sock, post + curpos, 1024, 0);
+            if (debug)
+                fprintf(stderr, "Sent %d bytes\n", socksent);
+      if (socksent < 0) {
+          perror(argv[1]);
+    close(sock);
+    free(post);
+    exit(5);
+            }
+  } else {
+      socksent = send(sock, post + curpos, postlen - curpos, 0);
+            if (debug)
+                fprintf(stderr, "Sent %d bytes\n", socksent);
+      if (socksent < 0) {
+          perror(argv[1]);
+    close(sock);
+    free(post);
+    exit(5);
+            }
+        }
+        curpos += socksent;
+        printf("\r%d/%d Bytes written: Upload %g%% complete        ", curpos, postlen, ((float)curpos / (float)postlen) * (float)100);
+  fflush(stdout);
+    }
+    printf("\nFinished sending post. Waiting for response.\n");
+    free(post);
+    regex_t pattern;
+    if (regcomp(&pattern, "count_down", REG_NOSUB)) {
+  printf("Error compiling expression to detect success or failure\n");
+  close(sock);
+  exit(7);
+    }
+    recvbuf[0] = 0;
+    recvlen = recv(sock, &recvbuf[0], 1024, MSG_WAITALL);
+    if (debug) {
+        fprintf(stderr, "Got %d bytes\n", recvlen);
+        fprintf(stderr, "%s", &recvbuf[0]);
+    }
+    int firstpacket = 1;
+    do {
+       if (recvlen < 0) {
+           perror(argv[1]);
+     close(sock);
+     exit(6);
+       } else if (recvlen > 0) {
+           if (!regexec(&pattern, &recvbuf[0], 0, NULL, 0)) {
+              printf("Firmware successfully sent.  Please wait for device to reboot.\n");
+              break;
+              if (firstpacket) {
+            printf("Error sending firmware to device.  Response is:\n");
+              }
+        printf("%s", &recvbuf[0]);
+          }
+          recvlen = recv(sock, &recvbuf[0], 1024, MSG_WAITALL);
+          if (debug) {
+        fprintf(stderr, "Got %d data\n", newrecvlen);
+              if (recvlen > 0)
+                 fprintf(stderr, "%s", &recvbuf[0]);
+          }
+       }
+       firstpacket = 0;
+    } while (recvlen > 0);
+    regfree(&pattern);
+    shutdown(sock, SHUT_RDWR);
+    close(sock);
+    return 0;    
+}

+ 11 - 0
openwrt/tools/opkg-link/opkg-jffs2

@@ -0,0 +1,11 @@
+#!/bin/sh
+JFFS=/jffs/
+
+# list all packages that have been installed additionally
+# to the squashfs (useful for upgrades)
+
+for LIST in $JFFS/usr/lib/opkg/info/*.list; do
+    PKG=${LIST##*/}
+    PKG=${PKG%.list}
+    echo $PKG
+done

+ 58 - 0
openwrt/tools/opkg-link/opkg-link

@@ -0,0 +1,58 @@
+#!/bin/sh
+#
+# Create symbolic links from all files in
+# alternative opkg destinations
+#
+# By Stefan Tomanek <stefan@pico.ruhr.de>
+
+# readlink might not be installed, so we probably
+# cannot detect whether a link points to a specific target
+if which readlink >/dev/null; then
+    points_to() {
+        local DST
+        local SRC
+        DST="$1"
+        SRC="$2"
+        [ `readlink "$DST"` = "$SRC" ]
+    }
+else
+    # we cannot determine the target of the link,
+    # so we return false to be on the safe side
+    false
+fi
+
+# find out the installation directories
+awk '$1 == "dest" && $3 != "/" { print $2, $3 }' /etc/opkg.conf | \
+while read DEST DDIR; do
+    echo "Processing destination $DEST..." >&2
+    # if the direktory does not exist, continue
+    [ -d "$DDIR" ] || continue
+
+    for LIST in "$DDIR/usr/lib/opkg/info"/*.list; do
+        [ -e "$LIST" ] || continue;
+        PKG=${LIST##*/}
+        PKG=${PKG%.list}
+        echo "  Linking package ${PKG} from $DEST..." >&2
+        while read FSRC; do
+            FDST=${FSRC#$DDIR}
+            
+            FDSTDIR=${FDST%/*}/
+            [ ! -d "$FDSTDIR" ] && {
+                echo "   Creating directory $FDSTDIR..." >&2
+                mkdir -p "$FDSTDIR"
+            }
+            if [ ! -e "$FDST" ] || [ -L "$FDST" ]; then
+                # do not rewrite every link
+                if [ -L "$FDST" ] && [ `readlink "$FDST"` = "$FSRC" ]; then
+                    :
+                    #echo "   $FDST already linked." >&2
+                else
+                    echo "   linking $FSRC -> $FDST" >&2
+                    ln -sf "$FSRC" "$FDST" 
+                fi
+            else
+                echo "   Not replacing existing file $FDST!" >&2
+            fi
+        done < "$LIST"
+  done
+done

+ 21 - 0
openwrt/tools/opkg-link/opkg-link-clean

@@ -0,0 +1,21 @@
+#!/bin/sh
+#
+# Find dangling symlinks that point into
+# opkg destination
+#
+# this script must be called with an pkg destination as first
+# argument
+#
+# By Stefan Tomanek <stefan@pico.ruhr.de>
+
+DEST=$1
+
+DDIR=`awk -vDEST=$DEST '$1 == "dest" && $2 == DEST { print $3 }' /etc/opkg.conf`
+
+[ -z "$DDIR" ] && exit 1
+
+find / -xdev -type l | while read L; do
+    readlink $L
+done | grep "^${DDIR}" | while read T; do
+    [ ! -e "$T" ] && echo ${T#$DDIR}
+done

+ 29 - 0
openwrt/tools/reghack/README.txt

@@ -0,0 +1,29 @@
+The reghack utility replaces the regulatory domain rules in the driver binaries
+with less restrictive ones. The current version also lifts the 5GHz radar
+channel restrictions in ath9k.
+
+This version is made for current OpenWrt trunk. To patch older release like 12.09,
+use http://luci.subsignal.org/~jow/reghack/old/ .
+
+How to use:
+
+ssh root@openwrt
+
+On ar71xx:
+
+cd /tmp/
+wget http://luci.subsignal.org/~jow/reghack/reghack.mips.elf
+chmod +x reghack.mips.elf
+./reghack.mips.elf /lib/modules/*/ath.ko
+./reghack.mips.elf /lib/modules/*/cfg80211.ko
+reboot
+
+On mpc85xx:
+
+cd /tmp/
+wget http://luci.subsignal.org/~jow/reghack/reghack.ppc.elf
+chmod +x reghack.ppc.elf
+./reghack.ppc.elf /lib/modules/*/ath.ko
+./reghack.ppc.elf /lib/modules/*/cfg80211.ko
+reboot
+

+ 392 - 0
openwrt/tools/reghack/reghack.c

@@ -0,0 +1,392 @@
+/*
+ * reghack - Utility to binary-patch the embedded mac80211 regulatory rules.
+ *
+ *   Copyright (C) 2012-2014 Jo-Philipp Wich <xm@subsignal.org>
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <byteswap.h>
+#include <limits.h>
+#include <arpa/inet.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+
+static int need_byteswap = 0;
+
+enum nl80211_dfs_regions {
+	NL80211_DFS_UNSET = 0,
+	NL80211_DFS_FCC = 1
+};
+
+struct ieee80211_freq_range {
+    uint32_t start_freq_khz;
+    uint32_t end_freq_khz;
+    uint32_t max_bandwidth_khz;
+};
+
+struct ieee80211_power_rule {
+    uint32_t max_antenna_gain;
+    uint32_t max_eirp;
+};
+
+struct ieee80211_reg_rule {
+    struct ieee80211_freq_range freq_range;
+    struct ieee80211_power_rule power_rule;
+    uint32_t flags;
+    uint32_t dfs_cac_ms;
+};
+
+struct ieee80211_regdomain {
+    uint32_t n_reg_rules;
+    char alpha2[2];
+    enum nl80211_dfs_regions dfs_region;
+    struct ieee80211_reg_rule reg_rules[1];
+};
+
+#define MHZ_TO_KHZ(freq) ((freq) * 1000)
+#define KHZ_TO_MHZ(freq) ((freq) / 1000)
+#define DBI_TO_MBI(gain) ((gain) * 100)
+#define MBI_TO_DBI(gain) ((gain) / 100)
+#define DBM_TO_MBM(gain) ((gain) * 100)
+#define MBM_TO_DBM(gain) ((gain) / 100)
+
+#define REG_RULE(start, end, bw, gain, eirp, reg_flags) \
+{                           \
+    .freq_range.start_freq_khz = MHZ_TO_KHZ(start), \
+    .freq_range.end_freq_khz = MHZ_TO_KHZ(end), \
+    .freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw), \
+    .power_rule.max_antenna_gain = DBI_TO_MBI(gain),\
+    .power_rule.max_eirp = DBM_TO_MBM(eirp),    \
+    .flags = reg_flags,             \
+    .dfs_cac_ms = 0, \
+}
+
+#define REG_MATCH(code, num, dfs, rule) \
+{ \
+	.alpha2 = code, \
+	.dfs_region = dfs, \
+	.n_reg_rules = num, \
+	.reg_rules = { \
+		rule \
+	} \
+}
+
+
+struct search_regdomain {
+	const char *desc;
+	struct ieee80211_regdomain reg;
+};
+
+static const struct search_regdomain search_regdomains[] = {
+	/* cfg80211.ko matches */
+	{
+		.desc = "core world5 regdomain in cfg80211/reg.o",
+		.reg  = REG_MATCH("00", 5, NL80211_DFS_UNSET, REG_RULE(2402, 2472, 40, 6, 20, 0))
+	}, {
+		.desc = "core world6 regdomain in cfg80211/reg.o",
+		.reg  = REG_MATCH("00", 6, NL80211_DFS_UNSET, REG_RULE(2402, 2472, 40, 6, 20, 0))
+	}, {
+		.desc = "embedded 00 regdomain in cfg80211/regdb.o",
+		.reg  = REG_MATCH("00", 5, NL80211_DFS_UNSET, REG_RULE(2402, 2472, 40, 3, 20, 0))
+	}, {
+		.desc = "embedded 00 regdomain in cfg80211/regdb.o",
+		.reg  = REG_MATCH("00", 6, NL80211_DFS_UNSET, REG_RULE(2402, 2472, 40, 3, 20, 0))
+	}, {
+		.desc = "embedded US regdomain in cfg80211/regdb.o",
+		.reg  = REG_MATCH("US", 6, NL80211_DFS_UNSET, REG_RULE(2402, 2472, 40, 3, 27, 0))
+	}, {
+		.desc = "embedded US regdomain in cfg80211/regdb.o",
+		.reg  = REG_MATCH("US", 7, NL80211_DFS_UNSET, REG_RULE(2402, 2472, 40, 3, 27, 0))
+	}, {
+		.desc = "embedded US regdomain in cfg80211/regdb.o",
+		.reg  = REG_MATCH("US", 7, NL80211_DFS_FCC, REG_RULE(2402, 2472, 40, 3, 27, 0))
+	},
+
+	/* regdb.txt matches (new) */
+	{
+		.desc = "embedded 00 regdomain in cfg80211/regdb.o",
+		.reg  = REG_MATCH("00", 6, NL80211_DFS_UNSET, REG_RULE(2402, 2472, 40, 0, 20, 0))
+	}, {
+		.desc = "embedded US regdomain in cfg80211/regdb.o",
+		.reg  = REG_MATCH("US", 5, NL80211_DFS_FCC, REG_RULE(2402, 2472, 40, 0, 30, 0))
+	},
+
+	/* ath.ko matches */
+	{
+		.desc = "ath world regdomain with 3 rules in ath/regd.o",
+		.reg  = REG_MATCH("99", 3, NL80211_DFS_UNSET, REG_RULE(2402, 2472, 40, 0, 20, 0))
+	}, {
+		.desc = "ath world regdomain with 4 rules in ath/regd.o",
+		.reg  = REG_MATCH("99", 4, NL80211_DFS_UNSET, REG_RULE(2402, 2472, 40, 0, 20, 0))
+	}, {
+		.desc = "ath world regdomain with 5 rules in ath/regd.o",
+		.reg  = REG_MATCH("99", 5, NL80211_DFS_UNSET, REG_RULE(2402, 2472, 40, 0, 20, 0))
+	}
+};
+
+
+struct search_insn {
+	const char *desc;
+	const uint16_t machine;
+	const uint32_t search;
+	const uint32_t replace;
+	const uint32_t mask;
+};
+
+static const struct search_insn search_insns[] = {
+	/* radar frequency check */
+	{
+		.desc    = "ath_is_radar_freq() MIPS opcode in ath/regd.o",
+		.machine = 0x0008,     /* MIPS */
+		.search  = 0x2400eb74, /* addiu rX, rY, -5260 */
+		.replace = 0x24000000, /* addiu rX, rY, 0	*/
+		.mask    = 0xfc00ffff
+	},
+	{
+		.desc    = "ath_is_radar_freq() PPC opcode in ath/regd.o",
+		.machine = 0x0014,     /* PPC */
+		.search  = 0x3800eb74, /* addi rX, rY, -5260 */
+		.replace = 0x38000000, /* addi rX, rY, 0 */
+		.mask    = 0xfc00ffff
+	},
+};
+
+
+static void check_endianess(unsigned char *elf_hdr)
+{
+	int self_is_be = (htonl(42) == 42);
+	int elf_is_be  = (elf_hdr[5] == 2);
+
+	if (self_is_be != elf_is_be)
+	{
+		need_byteswap = 1;
+		printf("Byte swapping needed (utility %s endian, module %s endian)\n",
+			   self_is_be ? "big" : "low",
+			   elf_is_be  ? "big" : "low");
+	}
+}
+
+static void bswap_rule(struct ieee80211_reg_rule *r)
+{
+	r->freq_range.start_freq_khz    = bswap_32(r->freq_range.start_freq_khz);
+	r->freq_range.end_freq_khz      = bswap_32(r->freq_range.end_freq_khz);
+	r->freq_range.max_bandwidth_khz = bswap_32(r->freq_range.max_bandwidth_khz);
+
+	r->power_rule.max_antenna_gain  = bswap_32(r->power_rule.max_antenna_gain);
+	r->power_rule.max_eirp          = bswap_32(r->power_rule.max_eirp);
+
+	r->flags                        = bswap_32(r->flags);
+}
+
+static int patch_regdomain(struct ieee80211_regdomain *pos,
+                           const struct ieee80211_regdomain *comp)
+{
+	struct ieee80211_reg_rule r2 = REG_RULE(2400, 2483, 40, 0, 30, 0);
+	struct ieee80211_reg_rule r5 = REG_RULE(5140, 5860, 40, 0, 30, 0);
+	struct ieee80211_regdomain pattern = *comp;
+
+	if (need_byteswap)
+	{
+		bswap_rule(&pattern.reg_rules[0]);
+		pattern.dfs_region = bswap_32(pattern.dfs_region);
+		pattern.n_reg_rules = bswap_32(pattern.n_reg_rules);
+	}
+
+	if (!memcmp(pos, &pattern, sizeof(pattern)))
+	{
+		pos->reg_rules[0] = r2;
+		pos->reg_rules[1] = r5;
+		pos->n_reg_rules = 2;
+		pos->dfs_region = 0;
+
+		if (need_byteswap)
+		{
+			bswap_rule(&pos->reg_rules[0]);
+			bswap_rule(&pos->reg_rules[1]);
+			pos->n_reg_rules = bswap_32(pos->n_reg_rules);
+		}
+
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static uint16_t check_ath_ko(unsigned char *elf_hdr, const char *filename)
+{
+	uint16_t type = *(uint16_t *)(elf_hdr + 18);
+	const char *file = strrchr(filename, '/');
+
+	if (!file)
+		file = filename;
+	else
+		file++;
+
+	if (need_byteswap)
+		type = bswap_16(type);
+
+	if (!strcmp(file, "ath.ko"))
+		return type;
+
+	return 0;
+}
+
+static int patch_insn(uint32_t *pos, const struct search_insn *insn)
+{
+	uint32_t cmp = need_byteswap ? bswap_32(*pos) : *pos;
+
+	if ((cmp & insn->mask) == insn->search)
+	{
+		*pos = need_byteswap ? bswap_32(insn->replace | (cmp & ~insn->mask))
+		                     : insn->replace | (cmp & ~insn->mask);
+
+		return 0;
+	}
+
+	return 1;
+}
+
+
+static int tryopen(const char *path, int *size, void **map)
+{
+	int fd;
+	struct stat s;
+
+	if (stat(path, &s))
+	{
+		perror("stat()");
+		return -1;
+	}
+
+	if ((fd = open(path, O_RDWR)) == -1)
+	{
+		perror("open()");
+		return -2;
+	}
+
+	*size = s.st_size;
+	*map = mmap(NULL, *size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+	if (*map == MAP_FAILED)
+	{
+		close(fd);
+		perror("mmap()");
+		return -3;
+	}
+
+	return fd;
+}
+
+int main(int argc, char **argv)
+{
+	int i, j, fd, sz;
+	int found = 0;
+	uint16_t ath_ko_machine = 0;
+
+	void *map;
+	char *tmp = NULL, cmd[PATH_MAX * 2 + 4];
+
+	if (argc < 2)
+	{
+		printf("Usage: %s module.ko\n", argv[0]);
+		exit(1);
+	}
+
+	fd = tryopen(argv[1], &sz, &map);
+
+	if (fd == -3)
+	{
+		printf("Memory mapping failed (missing fs support?), retrying from tmpfs\n");
+
+		tmp = tmpnam(NULL);
+
+		sprintf(cmd, "cp %s %s", argv[1], tmp);
+		system(cmd);
+
+		fd = tryopen(tmp, &sz, &map);
+	}
+
+	if (fd < 0)
+	{
+		if (tmp)
+			unlink(tmp);
+
+		exit(1);
+	}
+
+	check_endianess(map);
+	ath_ko_machine = check_ath_ko(map, argv[1]);
+
+	for (i = 0; i < (sz - sizeof(search_regdomains[0].reg)); i += sizeof(uint32_t))
+	{
+		if (ath_ko_machine)
+		{
+			for (j = 0; j < sizeof(search_insns)/sizeof(search_insns[0]); j++)
+			{
+				if (search_insns[j].machine != ath_ko_machine)
+					continue;
+
+				if (!patch_insn(map + i, &search_insns[j]))
+				{
+					printf("Patching @ 0x%08x: %s\n", i, search_insns[j].desc);
+					found = 1;
+				}
+			}
+		}
+
+		for (j = 0; j < (sizeof(search_regdomains)/sizeof(search_regdomains[0])); j++)
+		{
+			if (!patch_regdomain(map + i, &search_regdomains[j].reg))
+			{
+				printf("Patching @ 0x%08x: %s\n", i, search_regdomains[j].desc);
+				found = 1;
+			}
+		}
+	}
+
+	if (munmap(map, sz))
+	{
+		perror("munmap()");
+		exit(1);
+	}
+
+	if (tmp)
+	{
+		if (found)
+		{
+			sprintf(cmd, "cp %s %s", tmp, argv[1]);
+			system(cmd);
+		}
+
+		unlink(tmp);
+	}
+
+	close(fd);
+
+	if (!found)
+	{
+		printf("Unable to find regulatory rules (already patched?)\n");
+		exit(1);
+	}
+
+	return 0;
+}

BIN
openwrt/tools/reghack/reghack.mips.elf


BIN
openwrt/tools/reghack/reghack.ppc.elf