large scale android improvements

- Port vanilla-pipe on Android for users with rooted devices and compatible Wi-Fi hardware
- Drop OpenSSL as a build dependency (only used for hostap's crypto backend, but didn't work on Android so now we're using hostap's internal crypto)
- Improvements to event loop (targets 180Hz input polling + 60Hz video display)
- Implement MediaCodec hardware decoding on Android
- Use smaller and simpler DHCP client
This commit is contained in:
itsmattkc committed 2026-08-26 14:02:12 -07:00
1 parent 08d58d02e7
commit 1b2c4fa86c
58 files changed
+2308 -3420

No files matched your search

+6 -1
View File
@@ -19,9 +19,14 @@ else()
set(vendored_default FALSE)
endif()
set(vanilla_pipe_default OFF)
if(LINUX OR ANDROID)
set(vanilla_pipe_default ON)
endif()
OPTION(VANILLA_BUILD_GUI "Build SDL2 GUI app" ON)
OPTION(VANILLA_BUILD_TESTS "Build unit tests for Vanilla" OFF)
OPTION(VANILLA_BUILD_PIPE "Build vanilla-pipe for connecting to Wii U (Linux only)" ${LINUX})
OPTION(VANILLA_BUILD_PIPE "Build vanilla-pipe for connecting to Wii U" ${vanilla_pipe_default})
OPTION(VANILLA_BUILD_VENDORED "Build Vanilla with \"vendored\" third-party libraries" ${vendored_default})
add_subdirectory(lib)
+4 -4
View File
@@ -90,19 +90,19 @@ Vanilla currently requires the following dependencies:
- **Debian/Ubuntu**
```sh
apt install build-essential cmake libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev libavformat-dev libavcodec-dev libavutil-dev libswscale-dev libnl-genl-3-dev libnl-route-3-dev libssl-dev libxml2-dev libnm-dev libpolkit-agent-1-dev
apt install build-essential cmake libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev libavformat-dev libavcodec-dev libavutil-dev libswscale-dev libnl-genl-3-dev libnl-route-3-dev libxml2-dev libnm-dev libpolkit-agent-1-dev
```
- **Fedora**
```sh
dnf install libavcodec-free-devel libavutil-free-devel libavfilter-free-devel libnl3-devel SDL2-devel SDL2_image-devel SDL2_ttf-devel openssl-devel make automake gcc gcc-c++ kernel-devel cmake libxml2-devel NetworkManager-libnm-devel polkit-devel
dnf install libavcodec-free-devel libavutil-free-devel libavfilter-free-devel libnl3-devel SDL2-devel SDL2_image-devel SDL2_ttf-devel make automake gcc gcc-c++ kernel-devel cmake libxml2-devel NetworkManager-libnm-devel polkit-devel
```
- **Arch**
```sh
pacman -S base-devel make cmake ffmpeg libnl sdl2 sdl2_image sdl2_ttf libxml2 libnm openssl polkit
pacman -S base-devel make cmake ffmpeg libnl sdl2 sdl2_image sdl2_ttf libxml2 libnm polkit
```
- **Alpine/postmarketOS**
```sh
apk add build-base cmake sdl2-dev sdl2_image-dev sdl2_ttf-dev ffmpeg-dev libnl3-dev libxml2-dev openssl-dev networkmanager-dev polkit-dev
apk add build-base cmake sdl2-dev sdl2_image-dev sdl2_ttf-dev ffmpeg-dev libnl3-dev libxml2-dev networkmanager-dev polkit-dev
```
The build process is otherwise normal for a CMake program:
+9 -2
View File
@@ -13,7 +13,7 @@ android {
}
compileSdkVersion 34
defaultConfig {
minSdkVersion 19
minSdkVersion 21
targetSdkVersion 34
versionCode 1
versionName "1.0"
@@ -23,7 +23,7 @@ android {
// abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
// }
cmake {
arguments "-DANDROID_APP_PLATFORM=android-19", "-DANDROID_STL=c++_static"
arguments "-DANDROID_APP_PLATFORM=android-21", "-DANDROID_STL=c++_static"
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
version "3.31"
}
@@ -35,6 +35,13 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
packagingOptions {
jniLibs {
// Ensure libvanilla_pipe.so is accessible alongside main executable
useLegacyPackaging true
}
}
sourceSets.main.assets.setSrcDirs(['../../gui/assets'])
applicationVariants.all { variant ->
tasks["merge${variant.name.capitalize()}Assets"]
.dependsOn("externalNativeBuild${variant.name.capitalize()}")
+1 -1
View File
@@ -7,4 +7,4 @@
APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
# Min runtime API level
APP_PLATFORM=android-16
APP_PLATFORM=android-21
+2 -2
View File
@@ -9,5 +9,5 @@ project(GAME)
# Android has no need for a "windowed" option
set(VANILLA_GUI_ENABLE_WINDOWED OFF)
# Your game and its CMakeLists.txt are in a subfolder named "src"
add_subdirectory(../../.. "${CMAKE_SOURCE_DIR}/build")
# Add source files and keep in individual per-ABI dirs
add_subdirectory(../../.. "${CMAKE_CURRENT_BINARY_DIR}/vanilla")
+6
View File
@@ -96,3 +96,9 @@
void hapticRun(int, float, int);
void hapticStop(int);
}
-keepclassmembers class com.mattkc.vanilla.VanillaActivity {
public static android.view.Surface createVideoSurface(int, int, int);
public static float[] updateVideoSurfaceTexture();
public static void destroyVideoSurface();
}
@@ -1,18 +1,59 @@
package com.mattkc.vanilla;
import android.graphics.SurfaceTexture;
import android.view.Surface;
import java.util.concurrent.atomic.AtomicBoolean;
import org.libsdl.app.SDLActivity;
public class VanillaActivity extends SDLActivity {
private static SurfaceTexture videoSurfaceTexture;
private static Surface videoSurface;
private static final AtomicBoolean videoFrameAvailable = new AtomicBoolean(false);
private static final float[] videoTransform = new float[16];
@Override
protected String[] getLibraries() {
return new String[] {
"SDL2",
"SDL2_image",
// "SDL2_mixer",
// "SDL2_net",
"SDL2_ttf",
"xml2",
"vanilla"
};
}
public static synchronized Surface createVideoSurface(int textureName, int width, int height) {
destroyVideoSurface();
videoSurfaceTexture = new SurfaceTexture(textureName);
videoSurfaceTexture.setDefaultBufferSize(width, height);
videoSurfaceTexture.setOnFrameAvailableListener(surfaceTexture -> videoFrameAvailable.set(true));
videoSurface = new Surface(videoSurfaceTexture);
return videoSurface;
}
// Called from SDL render thread where owning GLES context is current
public static synchronized float[] updateVideoSurfaceTexture() {
if (videoSurfaceTexture == null || !videoFrameAvailable.getAndSet(false)) {
return null;
}
videoSurfaceTexture.updateTexImage();
videoSurfaceTexture.getTransformMatrix(videoTransform);
return videoTransform;
}
public static synchronized void destroyVideoSurface() {
videoFrameAvailable.set(false);
if (videoSurface != null) {
videoSurface.release();
videoSurface = null;
}
if (videoSurfaceTexture != null) {
videoSurfaceTexture.release();
videoSurfaceTexture = null;
}
}
}
+17 -8
View File
@@ -87,6 +87,11 @@ function(compile_ffmpeg _target_name _system_name _system_processor)
)
elseif(ANDROID)
set(FFMPEG_TARGET_OS "android")
list(APPEND FFMPEG_CONFIGURE_ARGS
--enable-jni
--enable-mediacodec
--enable-decoder=h264_mediacodec
)
# Get Android platform version (inexplicably apparently there literally isn't a better way to do this)
string(REPLACE "-" " " _android_platform_args ${ANDROID_PLATFORM})
@@ -296,6 +301,13 @@ list(APPEND VANILLA_GUI_SRC
ui/ui_keyboard.c
)
if (ANDROID)
list(APPEND VANILLA_GUI_SRC
platform_android.c
ui/ui_sdl_android.c
)
endif()
if (LINUX)
# Link with DRM if available
option(VANILLA_BUILD_USE_DRM "Enable DRM video output" ON)
@@ -386,7 +398,10 @@ target_link_libraries(vanillagui PRIVATE
if (ANDROID)
target_link_libraries(vanillagui PRIVATE
SDL2main
android
GLESv2
log
mediandk
)
endif()
@@ -487,19 +502,13 @@ target_include_directories(vanillagui PRIVATE
)
# Install assets
if(ANDROID)
# Put assets in correct place relative to build
add_custom_command(
TARGET vanilla POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_CURRENT_SOURCE_DIR}/assets" "${CMAKE_CURRENT_SOURCE_DIR}/../android/app/src/main/assets"
)
elseif(APPLE)
if(APPLE)
# Put assets in correct place relative to build
add_custom_command(
TARGET vanilla POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory_if_different "${CMAKE_CURRENT_SOURCE_DIR}/assets" "$<TARGET_FILE_DIR:vanilla>/../Resources/assets"
)
else()
elseif(NOT ANDROID)
# Put assets in correct place relative to build
add_custom_command(
TARGET vanilla POST_BUILD
+2 -1
View File
@@ -58,7 +58,8 @@ static const char *lang_str[__VPI_LANG_T_COUNT] = {
"Reset",
"Full Screen",
"Hardware Decoding",
"Cursor In Full Screen"
"Cursor In Full Screen",
"Local connection on Android requires root access."
};
const char *lang(vpi_lang_t id)
+1
View File
@@ -60,6 +60,7 @@ typedef enum {
VPI_LANG_FULLSCREEN,
VPI_LANG_HWDEC,
VPI_LANG_CURSOR_IN_FS,
VPI_LANG_ANDROID_ROOT_REQUIRED,
__VPI_LANG_T_COUNT
} vpi_lang_t;
+74 -9
View File
@@ -5,10 +5,14 @@
typedef uint32_t in_addr_t;
#else
#include <arpa/inet.h>
#ifdef ANDROID
#include <net/if.h>
#endif
#endif
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
@@ -17,6 +21,7 @@ typedef uint32_t in_addr_t;
#include "menu_common.h"
#include "menu_settings.h"
#include "pipemgmt.h"
#include "platform.h"
#include "ui/ui_anim.h"
#include "ui/ui_util.h"
@@ -26,8 +31,10 @@ static int error_lbl;
static int ip_textedit;
#define MAX_WIRELESS_ENTRIES 4
static size_t detected_wireless_interface_count = 0;
static char detected_wireless_interfaces[256][256];
static int intf_start = 0;
#define MAX_DETECTED_WIRELESS_INTERFACES (sizeof(detected_wireless_interfaces) / sizeof(detected_wireless_interfaces[0]))
static int intf_start = 0; // NOTE: Not currently implemented, but may be used for pagination in the future
static int intf_buttons[MAX_WIRELESS_ENTRIES];
static const char *checkmark = 0;//"checkmark.svg";
@@ -66,13 +73,39 @@ static void return_to_connection(vui_context_t *vui, int btn, void *v)
vui_transition_fade_layer_out(vui, layer, cb, cb_data);
}
static void populate_wireless_intf()
static void add_wireless_interface(const char *intf)
{
// LINUX ONLY:
// Sanity checks
if (!intf || !intf[0] || detected_wireless_interface_count >= MAX_DETECTED_WIRELESS_INTERFACES) {
return;
}
// Ensure interface hasn't already been added
for (size_t i = 0; i < detected_wireless_interface_count; i++) {
if (!strcmp(detected_wireless_interfaces[i], intf)) {
return;
}
}
// Add interface
vui_strncpy(
detected_wireless_interfaces[detected_wireless_interface_count],
intf,
sizeof(detected_wireless_interfaces[detected_wireless_interface_count]));
detected_wireless_interface_count++;
}
static void populate_wireless_intf(void)
{
#ifdef ANDROID
static const char *android_prefixes[] = {"wlan", "wifi", "swlan", "mlan", "ath"};
static const size_t android_prefix_count = sizeof(android_prefixes) / sizeof(android_prefixes[0]);
#endif
struct dirent *dir;
DIR *d = opendir("/sys/class/net");
char buf[1024];
size_t cur = 0;
detected_wireless_interface_count = 0;
if (d) {
while ((dir = readdir(d)) != 0) {
const char *intf = dir->d_name;
@@ -80,14 +113,38 @@ static void populate_wireless_intf()
snprintf(buf, sizeof(buf), "/sys/class/net/%s/wireless", intf);
DIR *wifi_check = opendir(buf);
if (wifi_check) {
vui_strncpy(detected_wireless_interfaces[cur], intf, sizeof(detected_wireless_interfaces[cur]));
cur++;
add_wireless_interface(intf);
closedir(wifi_check);
} else {
#ifdef ANDROID
// Fallback heuristic approach for Android
for (size_t prefix = 0; prefix < android_prefix_count; prefix++) {
const char *p = android_prefixes[prefix];
if (!strncmp(intf, p, strlen(p))) {
add_wireless_interface(intf);
break;
}
}
#endif
}
}
}
closedir(d);
}
#ifdef ANDROID
// Some Android releases may restrict /sys/class/net altogether. Probe
// heuristically.
for (size_t prefix = 0; prefix < android_prefix_count; prefix++) {
for (unsigned int index = 0; index < 8; index++) {
char intf[IF_NAMESIZE];
snprintf(intf, sizeof(intf), "%s%u", android_prefixes[prefix], index);
if (if_nametoindex(intf)) {
add_wireless_interface(intf);
}
}
}
#endif
}
static void intf_pressed(vui_context_t *vui, int button, void *v)
@@ -241,6 +298,7 @@ void vpi_menu_connection_and_return_to(vui_context_t *vui, int fade_fglayer, vui
int is_local = vpi_config.server_address == VANILLA_ADDRESS_LOCAL;
int is_via_server = vpi_config.server_address != VANILLA_ADDRESS_LOCAL;
int local_available = 1;
const int lbl2_width = bkg_rect.w*2/3;
const char *lbl2_txt;
@@ -250,6 +308,15 @@ void vpi_menu_connection_and_return_to(vui_context_t *vui, int fade_fglayer, vui
#else
lbl2_txt = lang(VPI_LANG_NO_LOCAL_AVAILABLE);
is_local = 0;
local_available = 0;
#endif
#ifdef ANDROID
if (local_available && !vpi_android_has_root_access()) {
lbl2_txt = lang(VPI_LANG_ANDROID_ROOT_REQUIRED);
is_local = 0;
local_available = 0;
}
#endif
vui_label_create(vui, bkg_rect.x + bkg_rect.w/2 - lbl2_width/2, bkg_rect.y + lbl_margin + bkg_rect.h/8, lbl2_width, bkg_rect.h - lbl_margin - lbl_margin, lbl2_txt, vui_color_create(0.66f,0.66f,0.66f,1), VUI_FONT_SIZE_SMALL, fglayer);
@@ -263,9 +330,7 @@ void vpi_menu_connection_and_return_to(vui_context_t *vui, int fade_fglayer, vui
vui_button_update_checked(vui, local_btn, is_local);
vui_button_update_checked(vui, via_server_btn, is_via_server);
#ifndef VANILLA_PIPE_AVAILABLE
vui_button_update_enabled(vui, local_btn, 0);
#endif
vui_button_update_enabled(vui, local_btn, local_available);
// Back button
vpi_menu_create_back_button(vui, fglayer, return_to_settings, (void *) (intptr_t) bglayer);
+64 -17
View File
@@ -5,6 +5,7 @@
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/hwcontext.h>
#include <libavutil/opt.h>
#include <libavutil/time.h>
#include <libswscale/swscale.h>
@@ -22,6 +23,11 @@
#include "ui/ui_anim.h"
#include "ui/ui_util.h"
#ifdef ANDROID
#include <libavutil/hwcontext_mediacodec.h>
#include "ui/ui_sdl_android.h"
#endif
#ifdef VANILLA_V4L2REQUEST_AVAILABLE
#include <fcntl.h>
#include <sys/ioctl.h>
@@ -242,6 +248,21 @@ static enum AVPixelFormat drm_get_format(struct AVCodecContext *s, const enum AV
return AV_PIX_FMT_NONE;
}
#ifdef ANDROID
static enum AVPixelFormat mediacodec_get_format(struct AVCodecContext *s,
const enum AVPixelFormat *fmt)
{
while (*fmt != AV_PIX_FMT_NONE) {
if (*fmt == AV_PIX_FMT_MEDIACODEC) {
return *fmt;
}
fmt++;
}
return AV_PIX_FMT_NONE;
}
#endif
typedef struct {
const char *name;
const AVCodec *codec;
@@ -249,6 +270,9 @@ typedef struct {
} hwdec_t;
enum HwDecoderType {
#ifdef ANDROID
HWDEC_TYPE_MEDIACODEC,
#endif
HWDEC_TYPE_NVDEC,
HWDEC_TYPE_VAAPI,
HWDEC_TYPE_V4L2REQUEST,
@@ -356,27 +380,24 @@ int open_decoder(vpi_decode_state_t *s, hwdec_t *dec)
s->codec_ctx->get_format = dec->get_format;
}
const int BUILD_AVCC = 0;
if (BUILD_AVCC) {
uint8_t sps[100], pps[100];
uint16_t sps_size = vanilla_generate_sps_params(sps, sizeof(sps));
uint16_t pps_size = vanilla_generate_pps_params(pps, sizeof(pps));
const uint8_t *p_sps = sps;
const uint8_t *p_pps = pps;
s->codec_ctx->extradata = av_malloc(1024);
s->codec_ctx->extradata_size = build_avcc(
s->codec_ctx->extradata, 1024,
&p_sps, &sps_size, 1,
&p_pps, &pps_size, 1,
4
);
if (!strcmp(dec->codec->name, "h264_mediacodec")) {
// MediaCodec requires the AVCC/SPS/PPS upfront
uint8_t header[200];
size_t header_size = vanilla_generate_h264_header(header, sizeof(header));
s->codec_ctx->extradata = av_mallocz(header_size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!s->codec_ctx->extradata) {
vpilog("Failed to allocate H.264 extradata\n");
return VANILLA_ERR_GENERIC;
}
memcpy(s->codec_ctx->extradata, header, header_size);
s->codec_ctx->extradata_size = (int) header_size;
s->codec_ctx->width = 854;
s->codec_ctx->height = 480;
}
ffmpeg_err = avcodec_open2(s->codec_ctx, dec->codec, NULL);
if (ffmpeg_err < 0) {
vpilog("Failed to open decoder: %i\n", ffmpeg_err);
vpilog("Failed to open decoder %s: %s (%i)\n", dec->name, av_err2str(ffmpeg_err), ffmpeg_err);
return VANILLA_ERR_GENERIC;
}
@@ -394,6 +415,12 @@ int vpi_decode_init(vpi_decode_state_t *s)
// Initialize decoding context, preferring hardware decoding when available
hwdec_t decoders[HWDEC_TYPE_COUNT];
#ifdef ANDROID
decoders[HWDEC_TYPE_MEDIACODEC].name = "MediaCodec";
decoders[HWDEC_TYPE_MEDIACODEC].codec = avcodec_find_decoder_by_name("h264_mediacodec");
decoders[HWDEC_TYPE_MEDIACODEC].get_format = mediacodec_get_format;
#endif
decoders[HWDEC_TYPE_NVDEC].name = "NVDEC";
decoders[HWDEC_TYPE_NVDEC].codec = avcodec_find_decoder_by_name("h264_cuvid");
decoders[HWDEC_TYPE_NVDEC].get_format = nvdec_get_format;
@@ -418,6 +445,26 @@ int vpi_decode_init(vpi_decode_state_t *s)
int r = VANILLA_ERR_GENERIC;
if (!vpi_config.force_software_decode) {
#ifdef ANDROID
// MediaCodec renders decoded buffers directly into the SurfaceTexture
// owned by SDL's external OES texture.
if (r != VANILLA_SUCCESS && vui_sdl_android_get_video_surface()) {
vpi_decode_exit(s);
s->hw_device_ctx = av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_MEDIACODEC);
if (s->hw_device_ctx) {
AVHWDeviceContext *device_ctx = (AVHWDeviceContext *) s->hw_device_ctx->data;
AVMediaCodecDeviceContext *mediacodec_ctx = (AVMediaCodecDeviceContext *) device_ctx->hwctx;
mediacodec_ctx->surface = vui_sdl_android_get_video_surface();
ffmpeg_err = av_hwdevice_ctx_init(s->hw_device_ctx);
if (ffmpeg_err >= 0) {
r = open_decoder(s, &decoders[HWDEC_TYPE_MEDIACODEC]);
} else {
vpilog("Failed to initialize MediaCodec device: %s\n", av_err2str(ffmpeg_err));
}
}
}
#endif
#ifdef VANILLA_CUDA_AVAILABLE
// See if we can create an NVDEC context (most NVIDIA GPUs)
if (r != VANILLA_SUCCESS) {
+77 -21
View File
@@ -1,3 +1,5 @@
#define _GNU_SOURCE
#include "pipemgmt.h"
#ifdef VANILLA_PIPE_AVAILABLE
@@ -15,6 +17,11 @@
#include <unistd.h>
#include <vanilla.h>
#ifdef ANDROID
#include <dlfcn.h>
#include <limits.h>
#endif
#include "config.h"
#include "platform.h"
@@ -193,7 +200,7 @@ void *vpi_pipe_log_thread(void *data)
if (buf[buf_read] == '\n') {
// Print line and reset
buf[buf_read + 1] = '\0';
vpilog(buf);
vpilog("%s", buf);
buf_read = 0;
} else {
@@ -270,9 +277,6 @@ void vpi_cancel_pw()
potential_pipe_pid = -1;
close(in_pipes[1]);
close(err_pipes[0]);
close(err_pipes[1]);
close(in_pipes[0]);
}
vpi_close_polkit_session();
}
@@ -290,19 +294,28 @@ int vpi_start_epilog()
int ret = VANILLA_ERR_GENERIC;
char ready_buf[500];
memset(ready_buf, 0, sizeof(ready_buf));
while (1) {
size_t read_count = 0;
int reached_eof = 0;
while (read_count + 1 < sizeof(ready_buf)) {
char c;
while (read_count < sizeof(ready_buf) && read(err_pipes[0], &c, 1) == 1) {
ssize_t read_size = read(err_pipes[0], &c, 1);
if (read_size < 0 && errno == EINTR) {
continue;
}
if (read_size != 1) {
reached_eof = 1;
break;
}
if (c == '\n') {
break;
}
ready_buf[read_count] = c;
read_count++;
ready_buf[read_count++] = c;
}
if (!memcmp(ready_buf, "READY", 5)) {
ready_buf[read_count] = 0;
if (!strcmp(ready_buf, "READY")) {
ret = VANILLA_SUCCESS;
pipe_input = in_pipes[1];
@@ -313,24 +326,27 @@ int vpi_start_epilog()
pipe_pid = potential_pipe_pid;
break;
} else {
// vpilog("GOT INVALID SIGNAL: %.*s\n", sizeof(ready_buf), ready_buf);
vpilog("%.*s\n", sizeof(ready_buf), ready_buf);
}
if (read_count) {
vpilog("%s\n", ready_buf);
}
if (reached_eof) {
break;
}
}
if (ret != VANILLA_SUCCESS) {
if (potential_pipe_pid != -1) {
kill(potential_pipe_pid, SIGKILL);
waitpid(potential_pipe_pid, NULL, 0);
potential_pipe_pid = -1;
}
close(in_pipes[1]);
close(err_pipes[0]);
break;
}
}
close(err_pipes[1]);
close(in_pipes[0]);
#ifdef VANILLA_POLKIT_AVAILABLE
vpi_close_polkit_session();
@@ -437,30 +453,70 @@ int vpi_start_pipe()
setsid();
// Get current working directory
// Execute process (this will replace the running code)
#ifdef ANDROID
// HACK: Use dladdr to get the path of us
Dl_info info;
if (!dladdr((void *) &vpi_start_pipe, &info) || !info.dli_fname) {
vpilog("Could not get running path of current Android executable\n");
return VANILLA_ERR_GENERIC;
}
// Copy path into our own string
char our_path[PATH_MAX];
if (snprintf(our_path, sizeof(our_path), "%s", info.dli_fname) >= sizeof(our_path)) {
vpilog("Android library path too long - this is a bug, tell maintainers\n");
return VANILLA_ERR_GENERIC;
}
// Get dirname of string and add libvanilla_pipe.so
char *library_dir = dirname(our_path);
char pipe_path[PATH_MAX];
if (snprintf(pipe_path, sizeof(pipe_path), "%s/libvanilla_pipe.so", library_dir) >= sizeof(pipe_path)) {
vpilog("Android pipe path too long - this is a bug, tell maintainers\n");
return VANILLA_ERR_GENERIC;
}
char command[PATH_MAX + 256];
snprintf(command, sizeof(command), "exec '%s' -local '%s'", pipe_path, vpi_config.wireless_interface);
r = execlp("su", "su", "-c", command, (const char *) 0);
#else
char exe[4096];
ssize_t link_len = readlink("/proc/self/exe", exe, sizeof(exe));
ssize_t link_len = readlink("/proc/self/exe", exe, sizeof(exe) - 1);
if (link_len <= 0) {
_exit(1);
}
exe[link_len] = 0;
dirname(exe);
strcat(exe, "/vanilla-pipe");
// Execute process (this will replace the running code)
#ifdef VANILLA_POLKIT_AVAILABLE
const char *pkexec = "pkexec";
r = execlp(pkexec, pkexec, exe, "-local", vpi_config.wireless_interface, (const char *) 0);
#else
r = execlp(exe, exe, "-local", vpi_config.wireless_interface, (const char *) 0);
#endif
#endif
// Handle failure to execute, use _exit so we don't interfere with the host
_exit(1);
} else if (pid < 0) {
// Fork error
close(in_pipes[0]);
close(in_pipes[1]);
close(err_pipes[0]);
close(err_pipes[1]);
return VANILLA_ERR_GENERIC;
} else {
// Continuation of parent
int ret = VANILLA_ERR_GENERIC;
// Only the child writes stderr and reads stdin. Closing the parent's
// copies here lets startup detect a child that exits before READY.
close(err_pipes[1]);
close(in_pipes[0]);
struct sigaction sa;
sa.sa_handler = sigint_handler;
sigemptyset(&sa.sa_mask);
+42
View File
@@ -0,0 +1,42 @@
#include "platform_android.h"
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <sys/wait.h>
// Check for whether we have root access on Android (required to run vanilla-pipe)
int vpi_android_has_root_access(void)
{
static int cached_result = -1;
if (cached_result >= 0) {
return cached_result;
}
// Attempt to run `id -u` as superuser - if it works, it'll return "0" meaning root
FILE *command = popen("su -c 'id -u' 2>/dev/null", "r");
if (!command) {
cached_result = 0;
return cached_result;
}
// See if command indeed returned "0"
int reported_root = 0;
char line[128];
while (fgets(line, sizeof(line), command)) {
char *end = NULL;
errno = 0;
long uid = strtol(line, &end, 10);
while (end && isspace((unsigned char) *end)) {
end++;
}
if (!errno && end != line && end && !*end && uid == 0) {
reported_root = 1;
}
}
// Clean up, cache and return result
int status = pclose(command);
cached_result = reported_root && status >= 0 && WIFEXITED(status) && WEXITSTATUS(status) == 0;
return cached_result;
}
+6
View File
@@ -0,0 +1,6 @@
#ifndef VANILLA_PI_PLATFORM_ANDROID_H
#define VANILLA_PI_PLATFORM_ANDROID_H
int vpi_android_has_root_access(void);
#endif // VANILLA_PI_PLATFORM_ANDROID_H
+231 -54
View File
@@ -1,7 +1,6 @@
#include "ui_sdl.h"
#include <stdatomic.h>
#include <errno.h>
#include <math.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_hints.h>
@@ -15,6 +14,11 @@
#include <libavutil/hwcontext.h>
#include <unistd.h>
#ifdef ANDROID
#include <libavcodec/mediacodec.h>
#include "ui_sdl_android.h"
#endif
#ifdef VANILLA_HAS_EGL
#include <SDL2/SDL_egl.h>
#include <SDL2/SDL_opengl.h>
@@ -51,6 +55,11 @@
#define AUDIO_BUFFER_SIZE 1664 // Wii U never deviates from this so we can hardcode it
#define AUDIO_BUFFER_COUNT 4
// Our tick rate is 180Hz, which matches the original gamepad input rate.
// Our render tick rate is 60Hz (or 1/3 the input tick rate).
#define VUI_INPUT_TICK_RATE 180
#define VUI_RENDER_TICK_DIVISOR 3
typedef struct {
SDL_Texture *texture;
int w;
@@ -80,12 +89,25 @@ typedef struct {
SDL_GameController *controller;
SDL_GameController *controller_gyros;
SDL_Texture *game_tex;
#ifdef ANDROID
SDL_Texture *android_video_tex;
float android_video_transform[16];
#endif
int last_shown_toast;
SDL_Texture *toast_tex;
struct timeval toast_expiry;
AVFrame *frame;
AVFrame *held_frame; // keeps the displayed dmabuf alive until the next frame replaces it
uint64_t present_frame_sequence;
Uint64 update_tick_origin;
Uint64 update_tick_frequency;
uint64_t update_tick;
#ifdef VANILLA_DRM_AVAILABLE
int fast_drm_active;
#endif
#ifdef ANDROID
int android_video_ready;
#endif
SDL_Texture *pw_tex;
uint8_t audio_buffer[AUDIO_BUFFER_COUNT][AUDIO_BUFFER_SIZE];
@@ -661,6 +683,15 @@ int vui_sdl_event_thread(void *data)
case SDL_TEXTEDITING:
vpilog("text editing!\n");
break;
case SDL_APP_WILLENTERBACKGROUND:
// On mobile, if the user is about to switch away from us,
// disconnect from the console so it doesn't remain burning up
// resources in the background
if (vui_game_mode_get(vui)) {
// Send shutdown signal
vpi_game_shutdown();
}
break;
}
}
// }
@@ -707,6 +738,14 @@ int vui_init_sdl(vui_context_t *ctx, int fullscreen)
// SDL_SetHintWithPriority("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD", "0", SDL_HINT_OVERRIDE);
SDL_SetHintWithPriority(SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES, "", SDL_HINT_OVERRIDE);
#ifdef ANDROID
// Force SDL to use landscape orientations only
SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeLeft LandscapeRight");
// Force opengles2 for hardware decode interoperability
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengles2");
#endif
// Force EGL when using X11 so that VAAPI works
SDL_SetHint(SDL_HINT_VIDEO_X11_FORCE_EGL, "1");
@@ -873,6 +912,13 @@ int vui_init_sdl(vui_context_t *ctx, int fullscreen)
sdl_ctx->game_tex = 0;
#ifdef ANDROID
sdl_ctx->android_video_tex = NULL;
if (vui_sdl_android_create_video_texture(sdl_ctx->renderer, ctx->screen_width, ctx->screen_height, &sdl_ctx->android_video_tex) < 0) {
vpilog("Android hardware decoding unavailable, falling back to software\n");
}
#endif
sdl_ctx->background = 0;
sdl_ctx->last_shown_toast = 0;
sdl_ctx->toast_tex = 0;
@@ -937,6 +983,13 @@ void vui_close_sdl(vui_context_t *ctx)
}
SDL_DestroyTexture(sdl_ctx->toast_tex);
#ifdef ANDROID
vui_sdl_android_destroy_video_texture();
if (sdl_ctx->game_tex == sdl_ctx->android_video_tex) {
sdl_ctx->game_tex = 0;
}
SDL_DestroyTexture(sdl_ctx->android_video_tex);
#endif
if (sdl_ctx->game_tex) SDL_DestroyTexture(sdl_ctx->game_tex);
SDL_DestroyTexture(sdl_ctx->background);
@@ -1270,14 +1323,21 @@ void vui_sdl_draw_background(vui_context_t *ctx, SDL_Renderer *renderer)
}
}
void vui_draw_sdl(vui_context_t *ctx, SDL_Renderer *renderer)
static SDL_Texture *vui_sdl_get_layer_texture(vui_context_t *ctx,
SDL_Renderer *renderer,
int layer)
{
vui_sdl_context_t *sdl_ctx = (vui_sdl_context_t *) ctx->platform_data;
if (sdl_ctx->layer_data[layer]) {
return sdl_ctx->layer_data[layer];
}
for (int layer = 0; layer < ctx->layers; layer++) {
sdl_ctx->layer_data[layer] = SDL_CreateTexture(
renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET,
ctx->screen_width, ctx->screen_height);
if (!sdl_ctx->layer_data[layer]) {
// Create a new layer here
sdl_ctx->layer_data[layer] = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, ctx->screen_width, ctx->screen_height);
return NULL;
}
SDL_BlendMode bm = SDL_ComposeCustomBlendMode(
SDL_BLENDFACTOR_ONE,
@@ -1288,10 +1348,18 @@ void vui_draw_sdl(vui_context_t *ctx, SDL_Renderer *renderer)
SDL_BLENDOPERATION_ADD
);
SDL_SetRenderDrawBlendMode(sdl_ctx->renderer, bm);
SDL_SetRenderDrawBlendMode(renderer, bm);
SDL_SetTextureBlendMode(sdl_ctx->layer_data[layer], bm);
return sdl_ctx->layer_data[layer];
}
SDL_SetRenderTarget(renderer, sdl_ctx->layer_data[layer]);
void vui_draw_sdl(vui_context_t *ctx, SDL_Renderer *renderer)
{
vui_sdl_context_t *sdl_ctx = (vui_sdl_context_t *) ctx->platform_data;
for (int layer = 0; layer < ctx->layers; layer++) {
SDL_Texture *layer_texture = vui_sdl_get_layer_texture(ctx, renderer, layer);
SDL_SetRenderTarget(renderer, layer_texture);
if (layer == 0 && ctx->background_enabled) {
// Draw background
@@ -1855,20 +1923,108 @@ int get_texture_from_drm_prime_frame(vui_sdl_context_t *sdl_ctx, AVFrame *f)
#endif
}
#ifdef ANDROID
static SDL_FPoint transform_android_texcoord(const float matrix[16],
float s, float t)
{
// Flip Y
t = 1.0f - t;
SDL_FPoint point = {
matrix[0] * s + matrix[4] * t + matrix[12],
matrix[1] * s + matrix[5] * t + matrix[13]
};
return point;
}
static int render_android_video_texture(vui_sdl_context_t *sdl_ctx,
int width, int height)
{
const float *matrix = sdl_ctx->android_video_transform;
const SDL_Color white = { 255, 255, 255, 255 };
SDL_Vertex vertices[4] = {
{ { 0.0f, 0.0f }, white, transform_android_texcoord(matrix, 0.0f, 0.0f) },
{ { (float) width, 0.0f }, white, transform_android_texcoord(matrix, 1.0f, 0.0f) },
{ { (float) width, (float) height }, white, transform_android_texcoord(matrix, 1.0f, 1.0f) },
{ { 0.0f, (float) height }, white, transform_android_texcoord(matrix, 0.0f, 1.0f) }
};
static const int indices[] = { 0, 1, 2, 0, 2, 3 };
if (SDL_RenderGeometry(sdl_ctx->renderer, sdl_ctx->android_video_tex, vertices, 4, indices, 6) < 0) {
vpilog("Failed to render Android external texture: %s\n", SDL_GetError());
return 0;
}
return 1;
}
#endif
static Uint64 vui_sdl_tick_deadline(const vui_sdl_context_t *sdl_ctx,
uint64_t tick)
{
Uint64 whole_seconds = tick / VUI_INPUT_TICK_RATE;
Uint64 partial_ticks = tick % VUI_INPUT_TICK_RATE;
return sdl_ctx->update_tick_origin
+ whole_seconds * sdl_ctx->update_tick_frequency
+ partial_ticks * sdl_ctx->update_tick_frequency
/ VUI_INPUT_TICK_RATE;
}
static void vui_sdl_wait_for_next_tick(vui_sdl_context_t *sdl_ctx)
{
sdl_ctx->update_tick++;
Uint64 now = SDL_GetPerformanceCounter();
Uint64 deadline = vui_sdl_tick_deadline(sdl_ctx, sdl_ctx->update_tick);
if (now >= deadline) {
Uint64 elapsed = now - sdl_ctx->update_tick_origin;
uint64_t elapsed_ticks =
(elapsed / sdl_ctx->update_tick_frequency) * VUI_INPUT_TICK_RATE
+ (elapsed % sdl_ctx->update_tick_frequency)
* VUI_INPUT_TICK_RATE / sdl_ctx->update_tick_frequency;
sdl_ctx->update_tick = elapsed_ticks + 1;
deadline = vui_sdl_tick_deadline(sdl_ctx, sdl_ctx->update_tick);
}
pthread_mutex_lock(&vpi_present_frame_mutex);
while ((now = SDL_GetPerformanceCounter()) < deadline) {
Uint64 remaining = deadline - now;
uint64_t wait_ns = remaining * 1000000000ULL
/ sdl_ctx->update_tick_frequency;
if (!wait_ns)
wait_ns = 1;
struct timespec timeout;
clock_gettime(CLOCK_REALTIME, &timeout);
timeout.tv_sec += wait_ns / 1000000000ULL;
timeout.tv_nsec += wait_ns % 1000000000ULL;
if (timeout.tv_nsec >= 1000000000L) {
timeout.tv_sec++;
timeout.tv_nsec -= 1000000000L;
}
pthread_cond_timedwait(&vpi_present_frame_cond, &vpi_present_frame_mutex, &timeout);
}
pthread_mutex_unlock(&vpi_present_frame_mutex);
}
// Rendering/main thread
int vui_update_sdl(vui_context_t *vui)
{
static Uint32 last_update_time = 0;
vui_sdl_event_thread(vui);
vui_sdl_context_t *sdl_ctx = (vui_sdl_context_t *) vui->platform_data;
if (!sdl_ctx->update_tick_frequency) {
sdl_ctx->update_tick_frequency = SDL_GetPerformanceFrequency();
sdl_ctx->update_tick_origin = SDL_GetPerformanceCounter();
}
int render_tick = sdl_ctx->update_tick % VUI_RENDER_TICK_DIVISOR == 0;
SDL_Renderer *renderer = sdl_ctx->renderer;
vui_update(vui);
SDL_Texture *main_tex;
SDL_Texture *main_tex = NULL;
#ifdef VANILLA_DRM_AVAILABLE
static vanilla_drm_ctx_t *drm_ctx = NULL;
@@ -1877,13 +2033,14 @@ int vui_update_sdl(vui_context_t *vui)
static vui_cuda_context_t *cuda_ctx = NULL;
#endif // VANILLA_CUDA_AVAILABLE
int handle_final_blit = 1;
int sdl_frame_ready = 0;
if (!vui->game_mode) {
#ifdef VANILLA_DRM_AVAILABLE
if (drm_ctx) {
vui_sdl_drm_free(&drm_ctx); // will set to null
}
sdl_ctx->fast_drm_active = 0;
#endif // VANILLA_DRM_AVAILABLE
#ifdef VANILLA_CUDA_AVAILABLE
if (cuda_ctx) {
@@ -1894,6 +2051,7 @@ int vui_update_sdl(vui_context_t *vui)
}
#endif // VANILLA_CUDA_AVAILABLE
if (render_tick) {
// Draw vui to a custom texture
vui_draw_sdl(vui, renderer);
@@ -1918,6 +2076,8 @@ int vui_update_sdl(vui_context_t *vui)
}
main_tex = sdl_ctx->layer_data[0];
sdl_frame_ready = main_tex != NULL;
}
} else {
pthread_mutex_lock(&vpi_present_frame_mutex);
if (vpi_present_frame
@@ -1948,14 +2108,15 @@ int vui_update_sdl(vui_context_t *vui)
SDL_RenderPresent(renderer);
}
}
if (drm_ctx &&
vui_sdl_drm_present(drm_ctx, sdl_ctx->frame)) {
handle_final_blit = 0;
if (drm_ctx && vui_sdl_drm_present(drm_ctx, sdl_ctx->frame)) {
sdl_ctx->fast_drm_active = 1;
} else {
sdl_ctx->fast_drm_active = 0;
get_texture_from_drm_prime_frame(sdl_ctx,
sdl_ctx->frame);
}
} else {
sdl_ctx->fast_drm_active = 0;
get_texture_from_drm_prime_frame(sdl_ctx, sdl_ctx->frame);
}
#endif // VANILLA_DRM_AVAILABLE
@@ -1989,6 +2150,22 @@ int vui_update_sdl(vui_context_t *vui)
}
break;
}
#ifdef ANDROID
case AV_PIX_FMT_MEDIACODEC:
{
AVMediaCodecBuffer *buffer = (AVMediaCodecBuffer *) sdl_ctx->frame->data[3];
if (!sdl_ctx->android_video_tex || !buffer) {
vpilog("MediaCodec produced a frame without an Android video surface\n");
break;
}
if (av_mediacodec_release_buffer(buffer, 1) < 0) {
vpilog("Failed to render MediaCodec output buffer\n");
break;
}
sdl_ctx->game_tex = sdl_ctx->android_video_tex;
break;
}
#endif
case AV_PIX_FMT_YUV420P:
get_texture_from_cpu_frame(sdl_ctx, sdl_ctx->frame);
break;
@@ -1996,23 +2173,51 @@ int vui_update_sdl(vui_context_t *vui)
av_frame_unref(sdl_ctx->held_frame);
av_frame_move_ref(sdl_ctx->held_frame, sdl_ctx->frame);
}
#ifdef ANDROID
/* MediaCodec publishes into SurfaceTexture asynchronously. Poll it at
* the input rate, but only draw the retained image on render ticks. */
if (sdl_ctx->game_tex == sdl_ctx->android_video_tex
&& SDL_RenderFlush(renderer) == 0
&& vui_sdl_android_update_video_texture(
sdl_ctx->android_video_transform)) {
sdl_ctx->android_video_ready = 1;
}
#endif
int sdl_handles_final_blit = 1;
#ifdef VANILLA_DRM_AVAILABLE
sdl_handles_final_blit = !sdl_ctx->fast_drm_active;
#endif
if (render_tick && sdl_handles_final_blit) {
main_tex = vui_sdl_get_layer_texture(vui, renderer, 0);
if (main_tex) {
SDL_SetRenderTarget(renderer, main_tex);
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
if (sdl_ctx->game_tex) {
if (handle_final_blit) {
main_tex = sdl_ctx->layer_data[0];
SDL_SetRenderTarget(renderer, main_tex);
SDL_RenderCopy(renderer, sdl_ctx->game_tex, 0, 0);
} else {
main_tex = sdl_ctx->game_tex;
#ifdef ANDROID
if (sdl_ctx->game_tex == sdl_ctx->android_video_tex) {
if (sdl_ctx->android_video_ready) {
render_android_video_texture(
sdl_ctx, vui->screen_width,
vui->screen_height);
}
} else
#endif
{
SDL_RenderCopy(renderer, sdl_ctx->game_tex, NULL,
NULL);
}
}
} else {
// Didn't get a frame, nothing to be done
handle_final_blit = 0;
sdl_frame_ready = 1;
}
}
}
if (handle_final_blit) {
if (sdl_frame_ready) {
// Draw toast on screen if necessary
const int TOAST_PADDING = 12;
int cur_toast;
@@ -2120,37 +2325,9 @@ int vui_update_sdl(vui_context_t *vui)
SDL_RenderPresent(renderer);
}
// Frame limiter to save CPU cycles
const Uint32 target = 5; // No need to update faster than 200Hz (gamepad polls at 180Hz, but this is easier to calculate)
Uint32 frame_delta = SDL_GetTicks() - last_update_time;
if (frame_delta < target) {
Uint32 wait_ms = target - frame_delta;
if (vui->game_mode) {
struct timespec deadline;
clock_gettime(CLOCK_REALTIME, &deadline);
deadline.tv_nsec += (long) wait_ms * 1000000;
if (deadline.tv_nsec >= 1000000000) {
deadline.tv_sec++;
deadline.tv_nsec -= 1000000000;
if (!vui->quit) {
vui_sdl_wait_for_next_tick(sdl_ctx);
}
pthread_mutex_lock(&vpi_present_frame_mutex);
while (sdl_ctx->present_frame_sequence == vpi_present_frame_sequence) {
int err = pthread_cond_timedwait(
&vpi_present_frame_cond,
&vpi_present_frame_mutex,
&deadline
);
if (err == ETIMEDOUT) {
break;
}
}
pthread_mutex_unlock(&vpi_present_frame_mutex);
} else {
SDL_Delay(wait_ms);
}
}
last_update_time = SDL_GetTicks();
return !vui->quit;
}
+195
View File
@@ -0,0 +1,195 @@
#include "ui_sdl_android.h"
#include <jni.h>
#include <SDL2/SDL_opengles2.h>
#include <SDL2/SDL_system.h>
#include <libavcodec/jni.h>
#include "platform.h"
static jclass video_class;
static jobject video_surface;
static jmethodID create_surface_method;
static jmethodID update_texture_method;
static jmethodID destroy_surface_method;
static int handle_jni_exception(JNIEnv *env, const char *operation)
{
if (!(*env)->ExceptionCheck(env)) {
return 0;
}
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
vpilog("Android video surface: %s failed\n", operation);
return -1;
}
static int init_jni(JNIEnv *env)
{
if (video_class) {
return 0;
}
jobject activity = (jobject) SDL_AndroidGetActivity();
if (!activity) {
vpilog("SDL_AndroidGetActivity returned NULL\n");
return -1;
}
jclass local_class = (*env)->GetObjectClass(env, activity);
(*env)->DeleteLocalRef(env, activity);
if (!local_class || handle_jni_exception(env, "looking up activity class") < 0) {
return -1;
}
video_class = (jclass) (*env)->NewGlobalRef(env, local_class);
(*env)->DeleteLocalRef(env, local_class);
if (!video_class) {
vpilog("Android video surface: could not retain activity class\n");
return -1;
}
create_surface_method = (*env)->GetStaticMethodID(env, video_class, "createVideoSurface", "(III)Landroid/view/Surface;");
update_texture_method = (*env)->GetStaticMethodID(env, video_class, "updateVideoSurfaceTexture", "()[F");
destroy_surface_method = (*env)->GetStaticMethodID(env, video_class, "destroyVideoSurface", "()V");
if (!create_surface_method || !update_texture_method || !destroy_surface_method ||
handle_jni_exception(env, "looking up video surface methods") < 0) {
(*env)->DeleteGlobalRef(env, video_class);
video_class = NULL;
return -1;
}
JavaVM *vm = NULL;
if ((*env)->GetJavaVM(env, &vm) != JNI_OK ||
av_jni_set_java_vm(vm, NULL) < 0) {
vpilog("Android video surface: could not register the Java VM with FFmpeg\n");
(*env)->DeleteGlobalRef(env, video_class);
video_class = NULL;
create_surface_method = NULL;
update_texture_method = NULL;
destroy_surface_method = NULL;
return -1;
}
return 0;
}
int vui_sdl_android_create_video_texture(SDL_Renderer *renderer, int width, int height, SDL_Texture **texture)
{
JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
SDL_RendererInfo info;
GLint texture_name = 0;
SDL_Texture *external_texture = NULL;
if (!env || !renderer || !texture || init_jni(env) < 0) {
goto die;
}
if (SDL_GetRendererInfo(renderer, &info) < 0) {
vpilog("Failed to determine SDL Android renderer info\n");
goto die;
}
if (SDL_strcmp(info.name, "opengles2") != 0) {
vpilog("Android video decoding requires opengles2 (currently using %s)\n", info.name);
goto die;
}
external_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_EXTERNAL_OES, SDL_TEXTUREACCESS_STATIC, width, height);
if (!external_texture) {
vpilog("Failed to create Android external texture: %s\n", SDL_GetError());
goto die;
}
SDL_RenderFlush(renderer);
if (SDL_GL_BindTexture(external_texture, NULL, NULL) < 0) {
vpilog("Failed to bind Android external texture: %s\n", SDL_GetError());
goto die_and_destroy_texture;
}
glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texture_name);
SDL_GL_UnbindTexture(external_texture);
if (!texture_name) {
vpilog("Failed to retrieve GL object from Android external texture\n");
goto die_and_destroy_texture;
}
jobject local_surface = (*env)->CallStaticObjectMethod(env, video_class, create_surface_method, (jint) texture_name, (jint) width, (jint) height);
if (handle_jni_exception(env, "creating SurfaceTexture") < 0 || !local_surface) {
goto die_and_destroy_texture;
}
video_surface = (*env)->NewGlobalRef(env, local_surface);
(*env)->DeleteLocalRef(env, local_surface);
if (!video_surface) {
(*env)->CallStaticVoidMethod(env, video_class, destroy_surface_method);
handle_jni_exception(env, "destroying unretained SurfaceTexture");
goto die_and_destroy_texture;
}
*texture = external_texture;
vpilog("Android hardware video decode surface initialized\n");
return 0;
die_and_destroy_texture:
SDL_DestroyTexture(external_texture);
die:
return -1;
}
void *vui_sdl_android_get_video_surface(void)
{
return video_surface;
}
int vui_sdl_android_update_video_texture(float transform[16])
{
JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
if (!env || !video_class || !video_surface) {
return 0;
}
jfloatArray matrix = (jfloatArray) (*env)->CallStaticObjectMethod(env, video_class, update_texture_method);
if (handle_jni_exception(env, "updating SurfaceTexture") < 0 || !matrix) {
return 0;
}
if ((*env)->GetArrayLength(env, matrix) != 16) {
(*env)->DeleteLocalRef(env, matrix);
vpilog("Android video surface returned an invalid transform matrix\n");
return 0;
}
(*env)->GetFloatArrayRegion(env, matrix, 0, 16, transform);
(*env)->DeleteLocalRef(env, matrix);
if (handle_jni_exception(env, "reading SurfaceTexture transform") < 0) {
return 0;
}
return 1;
}
void vui_sdl_android_destroy_video_texture(void)
{
JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
if (!env) {
return;
}
if (video_class && destroy_surface_method) {
(*env)->CallStaticVoidMethod(env, video_class, destroy_surface_method);
handle_jni_exception(env, "destroying SurfaceTexture");
}
if (video_surface) {
(*env)->DeleteGlobalRef(env, video_surface);
video_surface = NULL;
}
if (video_class) {
(*env)->DeleteGlobalRef(env, video_class);
video_class = NULL;
}
create_surface_method = NULL;
update_texture_method = NULL;
destroy_surface_method = NULL;
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef VANILLA_UI_SDL_ANDROID_H
#define VANILLA_UI_SDL_ANDROID_H
#include <SDL2/SDL.h>
int vui_sdl_android_create_video_texture(SDL_Renderer *renderer, int width,
int height, SDL_Texture **texture);
void *vui_sdl_android_get_video_surface(void);
int vui_sdl_android_update_video_texture(float transform[16]);
void vui_sdl_android_destroy_video_texture(void);
#endif
+37 -40
View File
@@ -37,38 +37,24 @@ static inline int skterr()
#endif
}
void create_sockaddr(sockaddr_u *addr, size_t *size, in_addr_t inaddr, uint16_t port, int local, int delete)
static void create_sockaddr(struct sockaddr_in *addr, size_t *size, in_addr_t inaddr, uint16_t port)
{
#ifndef _WIN32
if (local) {
memset(&addr->un, 0, sizeof(addr->un));
addr->un.sun_family = AF_UNIX;
snprintf(addr->un.sun_path, sizeof(addr->un.sun_path) - 1, VANILLA_PIPE_LOCAL_SOCKET, port);
if (delete)
unlink(addr->un.sun_path);
memset(addr, 0, sizeof(*addr));
addr->sin_family = AF_INET;
addr->sin_port = htons(port);
addr->sin_addr.s_addr = inaddr;
if (size) *size = sizeof(struct sockaddr_un);
} else {
#endif
memset(&addr->in, 0, sizeof(addr->in));
addr->in.sin_family = AF_INET;
addr->in.sin_port = htons(port);
addr->in.sin_addr.s_addr = inaddr;
if (size) *size = sizeof(struct sockaddr_in);
#ifndef _WIN32
}
#endif
if (size) *size = sizeof(*addr);
}
void create_server_sockaddr(sockaddr_u *addr, size_t *size, uint16_t port, int delete)
void create_server_sockaddr(struct sockaddr_in *addr, size_t *size, uint16_t port)
{
// The Wii U always places itself at this address
in_addr_t ip = (SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL) ? inet_addr("192.168.1.10") : SERVER_ADDRESS;
return create_sockaddr(addr, size, ip, port, 0, delete);
create_sockaddr(addr, size, ip, port);
}
void send_to_sockaddr(int fd, const void *data, size_t data_size, const sockaddr_u *sockaddr, size_t sockaddr_size)
void send_to_sockaddr(int fd, const void *data, size_t data_size, const struct sockaddr_in *sockaddr, size_t sockaddr_size)
{
ssize_t sent = sendto(fd, data, data_size, 0, (const struct sockaddr *) sockaddr, sockaddr_size);
if (sent == -1) {
@@ -81,12 +67,12 @@ void send_to_sockaddr(int fd, const void *data, size_t data_size, const sockaddr
void send_to_console(int fd, const void *data, size_t data_size, uint16_t port)
{
sockaddr_u addr;
struct sockaddr_in addr;
size_t addr_size;
in_port_t console_port = port - 100;
create_server_sockaddr(&addr, &addr_size, console_port, 0);
create_server_sockaddr(&addr, &addr_size, console_port);
send_to_sockaddr(fd, data, data_size, &addr, addr_size);
}
@@ -106,15 +92,14 @@ void set_socket_rcvtimeo(int skt, uint64_t microseconds)
int create_socket(int *socket_out, in_port_t port, int pipe)
{
sockaddr_u addr;
struct sockaddr_in addr;
size_t addr_size;
int is_pipe_and_local = (pipe && SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL);
int domain = is_pipe_and_local ? AF_UNIX : AF_INET;
in_addr_t bind_address = (pipe && SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL) ? htonl(INADDR_LOOPBACK) : INADDR_ANY;
create_sockaddr(&addr, &addr_size, INADDR_ANY, port, is_pipe_and_local, 1);
create_sockaddr(&addr, &addr_size, bind_address, port);
int skt = socket(domain, SOCK_DGRAM, 0);
int skt = socket(AF_INET, SOCK_DGRAM, 0);
if (skt == -1) {
vanilla_log("FAILED TO CREATE SOCKET: %i", skterr());
return VANILLA_ERR_BAD_SOCKET;
@@ -128,21 +113,21 @@ int create_socket(int *socket_out, in_port_t port, int pipe)
// vanilla_log("SUCCESSFULLY BOUND SOCKET %i ON PORT %i", skt, port);
(*socket_out) = skt;
set_socket_rcvtimeo(skt, 250000);
// int buf_sz = 16777216;
// setsockopt(skt, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz));
// setsockopt(skt, SOL_SOCKET, SO_SNDBUF, &buf_sz, sizeof(buf_sz));
#if !defined(_WIN32) && !defined(__APPLE__)
#if !defined(_WIN32) && !defined(__APPLE__) && !defined(ANDROID)
if (!pipe && SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL) {
// Bind to wireless device
setsockopt(skt, SOL_SOCKET, SO_BINDTODEVICE, wireless_interface, strlen(wireless_interface));
}
#endif
(*socket_out) = skt;
// int val = 1000; // microseconds to busy-poll per recv() before sleeping
// if (setsockopt(skt, SOL_SOCKET, SO_BUSY_POLL, &val, sizeof(val)) < 0) {
// perror("setsockopt SO_BUSY_POLL");
@@ -153,20 +138,19 @@ int create_socket(int *socket_out, in_port_t port, int pipe)
int send_pipe_cc(int skt, vanilla_pipe_command_t *cmd, size_t cmd_size, int wait_for_reply)
{
sockaddr_u addr;
struct sockaddr_in addr;
size_t addr_size;
int pipe_is_local = (SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL);
in_addr_t pipe_addr = pipe_is_local ? INADDR_ANY : SERVER_ADDRESS;
in_addr_t pipe_addr = (SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL) ? htonl(INADDR_LOOPBACK) : SERVER_ADDRESS;
create_sockaddr(&addr, &addr_size, pipe_addr, VANILLA_PIPE_CMD_SERVER_PORT, pipe_is_local, 0);
create_sockaddr(&addr, &addr_size, pipe_addr, VANILLA_PIPE_CMD_SERVER_PORT);
ssize_t read_size;
uint8_t recv_cc;
for (int retries = 0; retries < MAX_PIPE_RETRY; retries++) {
if (sendto(skt, (const char *) cmd, cmd_size, 0, (const struct sockaddr *) &addr, addr_size) == -1) {
vanilla_log("Failed to write control code to socket");
vanilla_log("Failed to write control code to socket: %i", skterr());
return 0;
}
@@ -178,8 +162,15 @@ int send_pipe_cc(int skt, vanilla_pipe_command_t *cmd, size_t cmd_size, int wait
if (recv_cc == VANILLA_PIPE_CC_BIND_ACK) {
return 1;
}
if (recv_cc == VANILLA_PIPE_CC_BUSY) {
return -1;
}
vanilla_log("STILL WAITING FOR REPLY");
if (read_size < 0) {
vanilla_log("STILL WAITING FOR REPLY: %i", skterr());
} else {
vanilla_log("UNEXPECTED PIPE REPLY: 0x%02x", recv_cc);
}
sleep(1);
}
@@ -205,7 +196,13 @@ int connect_to_backend(int *socket, vanilla_pipe_command_t *cmd, size_t cmd_size
set_socket_rcvtimeo(pipe_cc_skt, 2000000);
if (!send_pipe_cc(pipe_cc_skt, cmd, cmd_size, 1)) {
int send_result = send_pipe_cc(pipe_cc_skt, cmd, cmd_size, 1);
if (send_result < 0) {
vanilla_log("PIPE IS BUSY");
close(pipe_cc_skt);
return VANILLA_ERR_BUSY;
}
if (!send_result) {
vanilla_log("FAILED TO BIND TO PIPE");
close(pipe_cc_skt);
return VANILLA_ERR_PIPE_UNRESPONSIVE;
+2 -10
View File
@@ -11,7 +11,6 @@ typedef uint16_t in_port_t;
#else
#include <arpa/inet.h>
#include <errno.h>
#include <sys/un.h>
#endif
#include "vanilla.h"
@@ -55,18 +54,11 @@ typedef struct thread_data_t
vanilla_psk_t psk;
} thread_data_t;
typedef union {
struct sockaddr_in in;
#ifndef _WIN32
struct sockaddr_un un;
#endif
} sockaddr_u;
void sync_internal(thread_data_t *data);
void connect_as_gamepad_internal(thread_data_t *data);
int install_polkit_internal(thread_data_t *data, int install);
void create_server_sockaddr(sockaddr_u *addr, size_t *size, uint16_t port, int delete);
void send_to_sockaddr(int fd, const void *data, size_t data_size, const sockaddr_u *sockaddr, size_t sockaddr_size);
void create_server_sockaddr(struct sockaddr_in *addr, size_t *size, uint16_t port);
void send_to_sockaddr(int fd, const void *data, size_t data_size, const struct sockaddr_in *sockaddr, size_t sockaddr_size);
void send_to_console(int fd, const void *data, size_t data_size, uint16_t port);
int push_event(event_loop_t *loop, int type, const void *data, size_t size);
int get_event(event_loop_t *loop, vanilla_event_t *event, int wait);
+3 -3
View File
@@ -200,7 +200,7 @@ void set_battery_status(int status)
pthread_mutex_unlock(&button_mtx);
}
void send_input(int socket_hid, const sockaddr_u *addr, size_t addr_size)
void send_input(int socket_hid, const struct sockaddr_in *addr, size_t addr_size)
{
InputPacket ip;
memset(&ip, 0, sizeof(ip));
@@ -298,9 +298,9 @@ void *listen_input(void *x)
pthread_mutex_init(&button_mtx, NULL);
sockaddr_u addr;
struct sockaddr_in addr;
size_t addr_size;
create_server_sockaddr(&addr, &addr_size, PORT_HID - 100, 0);
create_server_sockaddr(&addr, &addr_size, PORT_HID - 100);
do {
send_input(info->socket_hid, &addr, addr_size);
-2
View File
@@ -20,8 +20,6 @@
#define VANILLA_PIPE_CC_UNINSTALL_POLKIT 0x8B
#define VANILLA_PIPE_CC_QUIT 0x90
#define VANILLA_PIPE_LOCAL_SOCKET "/tmp/vanilla-pipe_%i.sock"
#define POLKIT_ACTION_DST "/usr/share/polkit-1/actions/com.mattkc.vanilla.policy"
#define POLKIT_RULE_DST "/usr/share/polkit-1/rules.d/com.mattkc.vanilla.rules"
+66 -44
View File
@@ -1,15 +1,31 @@
include(ExternalProject)
# Add vanilla-pipe executalbe
# Add vanilla-pipe executable
add_executable(vanilla-pipe
dhcp/dhcp.c
main.c
wpa.c
)
if(ANDROID)
target_sources(vanilla-pipe PRIVATE android.c)
endif()
# Install vanilla-pipe
install(TARGETS vanilla-pipe)
option(VANILLA_BUILD_USE_LIBNM "Build with support for communicating to NetworkManager" ON)
# vanilla-pipe remains an executable, but is given a .so extension so Gradle will
# place it alongside main executable libvanilla.so
if(ANDROID)
set_target_properties(vanilla-pipe PROPERTIES
OUTPUT_NAME vanilla_pipe
PREFIX "lib"
SUFFIX ".so"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}"
)
endif()
option(VANILLA_BUILD_USE_LIBNM "Build with support for communicating to NetworkManager" ${LINUX})
if (VANILLA_BUILD_USE_LIBNM)
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBNM REQUIRED libnm)
@@ -18,34 +34,27 @@ if (VANILLA_BUILD_USE_LIBNM)
target_compile_definitions(vanilla-pipe PRIVATE USE_LIBNM)
endif()
if (VANILLA_BUILD_VENDORED)
# Build OpenSSL
set(OPENSSL_DIR ${CMAKE_CURRENT_BINARY_DIR}/openssl)
set(OPENSSL_INSTALL_DIR ${OPENSSL_DIR}/install)
set(OPENSSL_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR})
set(OPENSSL_EXTRA_FLAGS "")
if ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm")
set(OPENSSL_PROCESSOR "armv4")
set(OPENSSL_EXTRA_FLAGS "${CMAKE_C_FLAGS}")
endif()
set(OPENSSL_LIBRARIES "${OPENSSL_DIR}/libssl.a" "${OPENSSL_DIR}/libcrypto.a")
ExternalProject_Add(openssl
SOURCE_DIR ${OPENSSL_DIR}
BINARY_DIR ${OPENSSL_DIR}
GIT_REPOSITORY https://github.com/openssl/openssl.git
GIT_TAG openssl-3.5.0
CONFIGURE_COMMAND env CC=${CMAKE_C_COMPILER} ./Configure --prefix=${OPENSSL_INSTALL_DIR} linux-${OPENSSL_PROCESSOR}
INSTALL_COMMAND make install_sw
UPDATE_COMMAND ""
BUILD_BYPRODUCTS
${OPENSSL_LIBRARIES}
set(PIPE_TOOLCHAIN_FLAGS "")
if(ANDROID)
set(PIPE_TOOLCHAIN_FLAGS
"--target=${ANDROID_LLVM_TRIPLE} --sysroot=${CMAKE_SYSROOT}"
)
add_dependencies(vanilla-pipe openssl)
endif()
if (VANILLA_BUILD_VENDORED)
# Build LibNL
set(LIBNL_DIR ${CMAKE_CURRENT_BINARY_DIR}/libnl)
set(LIBNL_INSTALL_DIR ${LIBNL_DIR}/install)
if ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm")
set(LIBNL_EXTRA_FLAGS "")
set(LIBNL_CFLAGS "${CMAKE_C_FLAGS} ${PIPE_TOOLCHAIN_FLAGS}")
if(ANDROID)
set(LIBNL_EXTRA_FLAGS --disable-pthreads)
string(APPEND LIBNL_CFLAGS " -D__UAPI_DEF_IN_ADDR=0 -include bits/in_addr.h")
endif()
if (ANDROID)
set(LIBNL_HOST ${ANDROID_LLVM_TRIPLE})
elseif ("${CMAKE_SYSTEM_PROCESSOR}" STREQUAL "arm")
set(LIBNL_HOST arm-linux-gnueabihf)
else()
set(LIBNL_HOST ${CMAKE_SYSTEM_PROCESSOR}-linux-gnu)
@@ -59,7 +68,14 @@ if (VANILLA_BUILD_VENDORED)
BINARY_DIR ${LIBNL_DIR}
GIT_REPOSITORY https://github.com/thom311/libnl.git
GIT_TAG libnl3_11_0
CONFIGURE_COMMAND autoreconf -i && ./configure --prefix=${LIBNL_INSTALL_DIR} --host=${LIBNL_HOST}
PATCH_COMMAND autoreconf -i
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env
CC=${CMAKE_C_COMPILER}
AR=${CMAKE_AR}
RANLIB=${CMAKE_RANLIB}
"CFLAGS=${LIBNL_CFLAGS}"
"LDFLAGS=${CMAKE_EXE_LINKER_FLAGS}"
./configure --prefix=${LIBNL_INSTALL_DIR} --host=${LIBNL_HOST} --disable-shared --enable-static --disable-cli ${LIBNL_EXTRA_FLAGS}
UPDATE_COMMAND ""
BUILD_BYPRODUCTS
${LibNL_LIBRARY}
@@ -69,27 +85,33 @@ if (VANILLA_BUILD_VENDORED)
)
add_dependencies(vanilla-pipe libnl)
else()
# Find LibNL
# Find system LibNL
find_package(LibNL REQUIRED)
# Find OpenSSL and libnm
find_package(OpenSSL REQUIRED)
endif()
# Build WPA supplicant
set(HOSTAP_DIR ${CMAKE_CURRENT_BINARY_DIR}/hostap)
set(HOSTAP_CFLAGS "${PIPE_TOOLCHAIN_FLAGS}")
set(HOSTAP_ENV
CC=${CMAKE_C_COMPILER}
AR=${CMAKE_AR}
RANLIB=${CMAKE_RANLIB}
CONFIG_TLS=internal
CONFIG_CRYPTO=internal
CONFIG_INTERNAL_LIBTOMMATH=y
CONFIG_INTERNAL_LIBTOMMATH_FAST=y
)
if (VANILLA_BUILD_VENDORED)
set(HOSTAP_CFLAGS "-I${LIBNL_INSTALL_DIR}/include/libnl3 -I${OPENSSL_INSTALL_DIR}/include")
set(HOSTAP_LDFLAGS "-L${LIBNL_INSTALL_DIR}/lib -L${OPENSSL_INSTALL_DIR}/lib64 -L${OPENSSL_INSTALL_DIR}/lib")
string(APPEND HOSTAP_CFLAGS " -I${LIBNL_INSTALL_DIR}/include/libnl3")
set(HOSTAP_LDFLAGS "-L${LIBNL_INSTALL_DIR}/lib")
list(APPEND HOSTAP_ENV
PKG_CONFIG_PATH=${LIBNL_INSTALL_DIR}/lib/pkgconfig
EXTRA_CFLAGS=${HOSTAP_CFLAGS}
LDFLAGS=${HOSTAP_LDFLAGS}
)
endif()
if(HOSTAP_CFLAGS)
list(APPEND HOSTAP_ENV "EXTRA_CFLAGS=${HOSTAP_CFLAGS}")
endif()
find_package(Git REQUIRED)
ExternalProject_Add(hostap
SOURCE_DIR ${HOSTAP_DIR}
@@ -101,10 +123,11 @@ ExternalProject_Add(hostap
#GIT_REPOSITORY https://github.com/vanilla-wiiu/drc-hostap.git
DOWNLOAD_COMMAND
${CMAKE_COMMAND} -E remove_directory "${HOSTAP_DIR}" &&
${CMAKE_COMMAND} -E env --unset=GIT_DIR -- ${GIT_EXECUTABLE} clone https://github.com/vanilla-wiiu/drc-hostap.git "${HOSTAP_DIR}" &&
${CMAKE_COMMAND} -E env --unset=GIT_DIR -- ${GIT_EXECUTABLE} -C "${HOSTAP_DIR}" checkout 257096accc39f9c2750a7718ff5751108d15f668
${CMAKE_COMMAND} -E env --unset=GIT_DIR ${GIT_EXECUTABLE} clone https://github.com/vanilla-wiiu/drc-hostap.git "${HOSTAP_DIR}" &&
${CMAKE_COMMAND} -E env --unset=GIT_DIR ${GIT_EXECUTABLE} -C "${HOSTAP_DIR}" checkout a2f705daa143791180754100194c91714f7e8801
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E copy_if_different conf/wpa_supplicant.config wpa_supplicant/.config
BUILD_COMMAND env ${HOSTAP_ENV} make -C wpa_supplicant -j${CMAKE_BUILD_PARALLEL_LEVEL} libwpa_supplicant.a libwpa_client.a
BUILD_COMMAND env ${HOSTAP_ENV} make -C wpa_supplicant clean &&
env ${HOSTAP_ENV} make -C wpa_supplicant -j${CMAKE_BUILD_PARALLEL_LEVEL} libwpa_supplicant.a libwpa_client.a
INSTALL_COMMAND ""
UPDATE_COMMAND ""
BUILD_BYPRODUCTS
@@ -112,7 +135,7 @@ ExternalProject_Add(hostap
${HOSTAP_DIR}/wpa_supplicant/libwpa_client.a
)
if (VANILLA_BUILD_VENDORED)
add_dependencies(hostap openssl libnl)
add_dependencies(hostap libnl)
endif()
# Define wpa_client library from hostap
@@ -131,26 +154,25 @@ set_target_properties(wpa_supplicant_drc PROPERTIES
)
add_dependencies(wpa_supplicant_drc hostap)
# Compile custom dhclient library
add_subdirectory(dhcp EXCLUDE_FROM_ALL)
# Define vanilla-pipe include directories
target_include_directories(vanilla-pipe PRIVATE
${HOSTAP_DIR}/src
${HOSTAP_DIR}/src/common
${HOSTAP_DIR}/wpa_supplicant
${CMAKE_SOURCE_DIR}/lib
${CMAKE_CURRENT_SOURCE_DIR}/../../lib
${LibNL_INCLUDE_DIR}
)
# Link our library with the client library
target_link_libraries(vanilla-pipe PRIVATE
wpa_client
pthread
udhcpc
wpa_supplicant_drc
${LibNL_LIBRARY}
${LibNL_ROUTE_LIBRARY}
${LibNL_GENL_LIBRARY}
${OPENSSL_LIBRARIES}
${CMAKE_DL_LIBS}
m
)
if(NOT ANDROID)
target_link_libraries(vanilla-pipe PRIVATE pthread)
endif()
+472
View File
@@ -0,0 +1,472 @@
#include "android.h"
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <linux/fib_rules.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <limits.h>
#include <net/if.h>
#include <netlink/addr.h>
#include <netlink/netlink.h>
#include <netlink/route/nexthop.h>
#include <netlink/route/route.h>
#include <netlink/route/rule.h>
#include <netlink/socket.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include "wpa.h"
static int restore_wifi;
static int restore_wifi_scan_always;
#define ANDROID_WIFI_SHUTDOWN_TIMEOUT_MS 30000
#define ANDROID_WIFI_QUIET_PERIOD_MS 6000
#define ANDROID_WIFI_POLL_INTERVAL_US 250000
#define ANDROID_WIFI_STATUS_POLL_INTERVAL_MS 1000
#define VANILLA_CONSOLE_ADDRESS "192.168.1.10/32"
#define VANILLA_CONSOLE_RULE_PRIORITY 9999
static struct rtnl_route *console_route;
static struct rtnl_rule *console_rule;
typedef enum {
ANDROID_WIFI_STATE_UNKNOWN,
ANDROID_WIFI_STATE_DISABLED,
ANDROID_WIFI_STATE_NOT_DISABLED,
} android_wifi_state_t;
typedef enum {
ANDROID_WIFI_STATUS_UNAVAILABLE,
ANDROID_WIFI_STATUS_CMD,
ANDROID_WIFI_STATUS_DUMPSYS,
} android_wifi_status_source_t;
static struct rtnl_route *create_console_route(int ifindex,
struct nl_addr *destination,
struct nl_addr *source_address)
{
struct rtnl_route *route = rtnl_route_alloc();
struct rtnl_nexthop *next_hop = rtnl_route_nh_alloc();
if (!route || !next_hop) {
if (route)
rtnl_route_put(route);
if (next_hop)
rtnl_route_nh_free(next_hop);
return NULL;
}
rtnl_route_set_family(route, AF_INET);
rtnl_route_set_table(route, RT_TABLE_MAIN);
rtnl_route_set_protocol(route, RTPROT_STATIC);
rtnl_route_set_scope(route, RT_SCOPE_LINK);
rtnl_route_set_type(route, RTN_UNICAST);
if (rtnl_route_set_dst(route, destination) < 0 ||
rtnl_route_set_pref_src(route, source_address) < 0) {
rtnl_route_put(route);
rtnl_route_nh_free(next_hop);
return NULL;
}
rtnl_route_nh_set_ifindex(next_hop, ifindex);
rtnl_route_add_nexthop(route, next_hop);
return route;
}
static struct rtnl_rule *create_console_rule(struct nl_addr *destination)
{
struct rtnl_rule *rule = rtnl_rule_alloc();
if (rule) {
rtnl_rule_set_family(rule, AF_INET);
rtnl_rule_set_prio(rule, VANILLA_CONSOLE_RULE_PRIORITY);
rtnl_rule_set_table(rule, RT_TABLE_MAIN);
rtnl_rule_set_action(rule, FR_ACT_TO_TBL);
rtnl_rule_set_dst(rule, destination);
}
return rule;
}
int vanilla_android_install_console_routing(struct nl_sock *socket,
int ifindex,
struct nl_addr *source_address)
{
// Remove any previously made routing (during this session)
vanilla_android_remove_console_routing(socket);
// Sanity check input
struct nl_addr *destination = NULL;
if (!socket || ifindex <= 0 || !source_address || nl_addr_parse(VANILLA_CONSOLE_ADDRESS, AF_INET, &destination) < 0) {
nlprint("ANDROID: INVALID INPUT");
goto die;
}
// Build route and rule
struct rtnl_route *route = create_console_route(ifindex, destination, source_address);
struct rtnl_rule *rule = create_console_rule(destination);
nl_addr_put(destination);
if (!route || !rule) {
nlprint("ANDROID: FAILED TO BUILD WII U ROUTING");
if (route) {
rtnl_route_put(route);
}
if (rule) {
rtnl_rule_put(rule);
}
goto die;
}
// Remove an existing rule if a previous pipe was killed without cleaning up
rtnl_rule_delete(socket, rule, 0);
// Add route
int result = rtnl_route_add(socket, route, NLM_F_CREATE | NLM_F_REPLACE);
if (result < 0) {
nlprint("ANDROID: FAILED TO ADD WII U ROUTE: %s", nl_geterror(result));
goto die_and_put;
}
// Add rule
result = rtnl_rule_add(socket, rule, NLM_F_CREATE | NLM_F_EXCL);
if (result < 0) {
nlprint("ANDROID: FAILED TO ADD WII U ROUTING RULE: %s", nl_geterror(result));
goto die_and_delete_route;
}
// Everything worked, store route/rule so we can remove later
console_route = route;
console_rule = rule;
nlprint("ANDROID: NETWORK ROUTE AND RULE ADDED");
return 0;
die_and_delete_route:
rtnl_route_delete(socket, route, 0);
die_and_put:
rtnl_route_put(route);
rtnl_rule_put(rule);
die:
return -1;
}
void vanilla_android_remove_console_routing(struct nl_sock *socket)
{
if (!socket) {
return;
}
if (console_rule) {
rtnl_rule_delete(socket, console_rule, 0);
rtnl_rule_put(console_rule);
console_rule = NULL;
}
if (console_route) {
rtnl_route_delete(socket, console_route, 0);
rtnl_route_put(console_route);
console_route = NULL;
}
}
static android_wifi_state_t parse_wifi_state(const char *status)
{
if (strstr(status, "Wifi is disabled") ||
strstr(status, "Wi-Fi is disabled"))
return ANDROID_WIFI_STATE_DISABLED;
if (strstr(status, "Wifi is enabled") ||
strstr(status, "Wi-Fi is enabled") ||
strstr(status, "Wifi is enabling") ||
strstr(status, "Wi-Fi is enabling") ||
strstr(status, "Wifi is disabling") ||
strstr(status, "Wi-Fi is disabling"))
return ANDROID_WIFI_STATE_NOT_DISABLED;
return ANDROID_WIFI_STATE_UNKNOWN;
}
static android_wifi_state_t query_wifi_state(android_wifi_status_source_t source)
{
const char **command;
const char *cmd_status[] = {"cmd", "wifi", "status", NULL};
const char *dumpsys_status[] = {"dumpsys", "wifi", NULL};
if (source == ANDROID_WIFI_STATUS_CMD) {
command = cmd_status;
} else if (source == ANDROID_WIFI_STATUS_DUMPSYS) {
command = dumpsys_status;
} else {
return ANDROID_WIFI_STATE_UNKNOWN;
}
char status[256];
ssize_t status_len = run_process_and_read_stdout(
command, status, sizeof(status) - 1);
if (status_len <= 0)
return ANDROID_WIFI_STATE_UNKNOWN;
status[status_len] = 0;
return parse_wifi_state(status);
}
static android_wifi_status_source_t find_wifi_status_source(android_wifi_state_t *state)
{
*state = query_wifi_state(ANDROID_WIFI_STATUS_CMD);
if (*state != ANDROID_WIFI_STATE_UNKNOWN) {
return ANDROID_WIFI_STATUS_CMD;
}
*state = query_wifi_state(ANDROID_WIFI_STATUS_DUMPSYS);
if (*state != ANDROID_WIFI_STATE_UNKNOWN) {
return ANDROID_WIFI_STATUS_DUMPSYS;
}
return ANDROID_WIFI_STATUS_UNAVAILABLE;
}
static int query_wifi_enabled_setting(int *enabled)
{
char status[64];
ssize_t status_len = run_process_and_read_stdout((const char *[]) {"settings", "get", "global", "wifi_on", NULL}, status, sizeof(status) - 1);
if (status_len <= 0)
return -1;
status[status_len] = 0;
char *end;
errno = 0;
long value = strtol(status, &end, 10);
while (*end && isspace((unsigned char) *end)) {
end++;
}
if (errno || end == status || *end) {
return -1;
}
// WifiSettingsStore uses 1 for enabled and 3 for enabled while overriding
// airplane mode (0 and 2 mean disabled)
*enabled = value == 1 || value == 3;
return 0;
}
static int query_wifi_scan_always_setting(int *enabled)
{
char status[64];
ssize_t status_len = run_process_and_read_stdout((const char *[]) {"settings", "get", "global", "wifi_scan_always_enabled", NULL}, status, sizeof(status) - 1);
if (status_len <= 0) {
return -1;
}
status[status_len] = 0;
char *end;
errno = 0;
long value = strtol(status, &end, 10);
while (*end && isspace((unsigned char) *end)) {
end++;
}
if (errno || end == status || *end) {
return -1;
}
*enabled = value != 0;
return 0;
}
static int set_wifi_scan_always_setting(int enabled)
{
return run_process_and_read_stdout((const char *[]) {"settings", "put", "global", "wifi_scan_always_enabled", enabled ? "1" : "0", NULL}, NULL, 0) < 0 ? -1 : 0;
}
static void restore_android_wifi_state(void)
{
if (restore_wifi) {
nlprint("ANDROID: RESTORING THE SYSTEM WI-FI");
run_process_and_read_stdout((const char *[]) {"svc", "wifi", "enable", NULL}, NULL, 0);
restore_wifi = 0;
}
if (restore_wifi_scan_always) {
nlprint("ANDROID: RESTORING BACKGROUND WI-FI SCANNING");
set_wifi_scan_always_setting(1);
restore_wifi_scan_always = 0;
}
}
static int process_named_is_running(const char *name)
{
DIR *proc = opendir("/proc");
if (!proc) {
return 0;
}
int found = 0;
struct dirent *entry;
while (!found && (entry = readdir(proc))) {
if (!isdigit((unsigned char) entry->d_name[0])) {
continue;
}
char comm_path[PATH_MAX];
if (snprintf(comm_path, sizeof(comm_path), "/proc/%s/comm", entry->d_name) >= (int) sizeof(comm_path)) {
continue;
}
FILE *comm = fopen(comm_path, "r");
if (!comm) {
continue;
}
char process_name[64];
if (fgets(process_name, sizeof(process_name), comm)) {
process_name[strcspn(process_name, "\r\n")] = 0;
found = !strcmp(process_name, name);
}
fclose(comm);
}
closedir(proc);
return found;
}
static int stock_supplicant_is_running(void)
{
return process_named_is_running("wpa_supplicant") || process_named_is_running("p2p_supplicant");
}
static int64_t monotonic_milliseconds(void)
{
struct timespec now;
if (clock_gettime(CLOCK_MONOTONIC, &now) < 0) {
return -1;
}
return (int64_t) now.tv_sec * 1000 + now.tv_nsec / 1000000;
}
static int wait_for_wifi_shutdown(android_wifi_status_source_t status_source)
{
int64_t started_at = monotonic_milliseconds();
int64_t quiet_since = -1;
int64_t next_status_check = started_at;
android_wifi_state_t last_state = ANDROID_WIFI_STATE_UNKNOWN;
int supplicant_running = 0;
if (started_at < 0) {
return -1;
}
nlprint("ANDROID: WAITING FOR THE SYSTEM WI-FI TO RELEASE WLAN");
while (1) {
int64_t now = monotonic_milliseconds();
if (now < 0) {
return -1;
}
if (status_source != ANDROID_WIFI_STATUS_UNAVAILABLE
&& now >= next_status_check) {
last_state = query_wifi_state(status_source);
now = monotonic_milliseconds();
if (now < 0) {
return -1;
}
next_status_check = now + ANDROID_WIFI_STATUS_POLL_INTERVAL_MS;
}
// Some Android builds may keep an idle supplicant service alive even
// after disabling Wi-Fi. Check for it.
supplicant_running = status_source == ANDROID_WIFI_STATUS_UNAVAILABLE && stock_supplicant_is_running();
// Some Android builds may attempt to recover the Wi-Fi, wait to ensure
// that doesn't happen
int framework_disabled = status_source == ANDROID_WIFI_STATUS_UNAVAILABLE || last_state == ANDROID_WIFI_STATE_DISABLED;
if (framework_disabled && !supplicant_running) {
if (quiet_since < 0) {
quiet_since = now;
}
if (now - quiet_since >= ANDROID_WIFI_QUIET_PERIOD_MS) {
nlprint("ANDROID: SYSTEM WI-FI RELEASED WLAN");
return 0;
}
} else {
quiet_since = -1;
}
if (now - started_at >= ANDROID_WIFI_SHUTDOWN_TIMEOUT_MS) {
break;
}
usleep(ANDROID_WIFI_POLL_INTERVAL_US);
}
if (status_source == ANDROID_WIFI_STATUS_UNAVAILABLE) {
nlprint("ANDROID: TIMED OUT WAITING FOR THE SYSTEM WI-FI "
"(supplicant=%s)",
supplicant_running ? "running" : "stopped");
} else {
nlprint("ANDROID: TIMED OUT WAITING FOR THE SYSTEM WI-FI "
"(state=%s)",
last_state == ANDROID_WIFI_STATE_DISABLED ? "disabled" :
last_state == ANDROID_WIFI_STATE_NOT_DISABLED ? "active" :
"unknown");
}
return -1;
}
int vanilla_android_acquire_wifi(const char *wireless_interface)
{
android_wifi_state_t initial_state;
android_wifi_status_source_t status_source = find_wifi_status_source(&initial_state);
if (query_wifi_enabled_setting(&restore_wifi) < 0) {
restore_wifi = initial_state != ANDROID_WIFI_STATE_DISABLED;
}
// Android may continue doing location scans even while Wi-Fi is off,
// conflicting with us. Ensure background scanning is also off.
restore_wifi_scan_always = 0;
int scan_always_enabled;
if (query_wifi_scan_always_setting(&scan_always_enabled) == 0 &&
scan_always_enabled) {
nlprint("ANDROID: TEMPORARILY DISABLING BACKGROUND WI-FI SCANNING");
if (set_wifi_scan_always_setting(0) < 0) {
nlprint("ANDROID: FAILED TO DISABLE BACKGROUND WI-FI SCANNING");
return -1;
}
restore_wifi_scan_always = 1;
}
nlprint("ANDROID: TEMPORARILY DISABLING THE SYSTEM WI-FI");
if (run_process_and_read_stdout(
(const char *[]) {"svc", "wifi", "disable", NULL}, NULL, 0) < 0) {
nlprint("ANDROID: FAILED TO DISABLE THE SYSTEM WI-FI");
restore_android_wifi_state();
return -1;
}
if (wait_for_wifi_shutdown(status_source) < 0) {
restore_android_wifi_state();
return -1;
}
if (!if_nametoindex(wireless_interface)) {
nlprint("ANDROID: INTERFACE %s DISAPPEARED WHEN WI-FI WAS DISABLED; "
"THIS DEVICE NEEDS A VENDOR-SPECIFIC RADIO BACKEND",
wireless_interface);
restore_android_wifi_state();
return -1;
}
return 0;
}
void vanilla_android_release_wifi(void)
{
restore_android_wifi_state();
}
+15
View File
@@ -0,0 +1,15 @@
#ifndef VANILLA_PIPE_ANDROID_H
#define VANILLA_PIPE_ANDROID_H
struct nl_sock;
struct nl_addr;
int vanilla_android_acquire_wifi(const char *wireless_interface);
void vanilla_android_release_wifi(void);
int vanilla_android_install_console_routing(struct nl_sock *socket,
int ifindex,
struct nl_addr *source_address);
void vanilla_android_remove_console_routing(struct nl_sock *socket);
#endif // VANILLA_PIPE_ANDROID_H
-13
View File
@@ -1,13 +0,0 @@
udhcp server/client package
-----------------------
Russ Dill <Russ.Dill@asu.edu>
Matthew Ramsay <matthewr@moreton.com.au>
Chris Trew <christ@moreton.com.au>
Other Credits:
--------------
Moreton Bay (http://www.moretonbay.com/)
Vladimir Oleynik <dzo@simtrea.ru> Size optimizations
-14
View File
@@ -1,14 +0,0 @@
add_library(udhcpc STATIC
common.c
options.c
packet.c
pidfile.c
signalpipe.c
socket.c
dhcpc.c
clientpacket.c
clientsocket.c
script.c
)
target_compile_definitions(udhcpc PRIVATE _GNU_SOURCE $<$<CONFIG:Debug>:UDHCP_DEBUG>)
-339
View File
@@ -1,339 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
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 2 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, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
-142
View File
@@ -1,142 +0,0 @@
udhcp client (udhcpc)
--------------------
The udhcp client negotiates a lease with the DHCP server and notifies
a set of scripts when a leases is obtained or lost.
command line options
-------------------
The command line options for the udhcp client are:
-c, --clientid=CLIENTID Client identifier
-H, --hostname=HOSTNAME Client hostname
-h, Alias for -H
-F, --fqdn=FQDN Client fully qualified domain name
-f, --foreground Do not fork after getting lease
-b, --background Fork to background if lease cannot be
immediately negotiated.
-i, --interface=INTERFACE Interface to use (default: eth0)
-n, --now Exit with failure if lease cannot be
immediately negotiated.
-p, --pidfile=file Store process ID of daemon in file
-q, --quit Quit after obtaining lease
-r, --request=IP IP address to request (default: none)
-s, --script=file Run file at dhcp events (default:
/etc/udhcpc/default.script)
-v, --version Display version
If the requested IP address cannot be obtained, the client accepts the
address that the server offers.
udhcp client scripts
-------------------
When an event occurs, udhcpc calls the action script. udhcpc never does
any configuration of the network interface itself, but instead relies on
a set of scripts. The script by default is
/etc/udhcpc/default.script but this can be changed via the command
line arguments. The three possible arguments to the script are:
deconfig: This argument is used when udhcpc starts, and
when a leases is lost. The script must put the interface in an
up, but deconfigured state, ie: ifconfig $interface 0.0.0.0.
bound: This argument is used when udhcpc moves from an
unbound, to a bound state. All of the paramaters are set in
enviromental variables, The script should configure the interface,
and set any other relavent parameters (default gateway, dns server,
etc).
renew: This argument is used when a DHCP lease is renewed. All of
the paramaters are set in enviromental variables. This argument is
used when the interface is already configured, so the IP address,
will not change, however, the other DHCP paramaters, such as the
default gateway, subnet mask, and dns server may change.
nak: This argument is used with udhcpc receives a NAK message.
The script with the deconfig argument will be called directly
afterwards, so no changes to the network interface are neccessary.
This hook is provided for purely informational purposes (the
message option may contain a reason for the NAK).
The paramaters for enviromental variables are as follows:
$HOME - The set $HOME env or "/"
$PATH - the set $PATH env or "/bin:/usr/bin:/sbin:/usr/sbin"
$1 - What action the script should perform
interface - The interface this was obtained on
ip - The obtained IP
mask - The number of bits in the netmask (ie: 24)
siaddr - The bootp next server option
sname - The bootp server name option
boot_file - The bootp boot file option
subnet - The assigend subnet mask
timezone - Offset in seconds from UTC
router - A list of routers
timesvr - A list of time servers
namesvr - A list of IEN 116 name servers
dns - A list of DNS server
logsvr - A list of MIT-LCS UDP log servers
cookiesvr - A list of RFC 865 cookie servers
lprsvr - A list of LPR servers
hostname - The assigned hostname
bootsize - The length in 512 octect blocks of the bootfile
domain - The domain name of the network
swapsvr - The IP address of the client's swap server
rootpath - The path name of the client's root disk
ipttl - The TTL to use for this network
mtu - The MTU to use for this network
broadcast - The broadcast address for this network
ntpsrv - A list of NTP servers
wins - A list of WINS servers
lease - The lease time, in seconds
dhcptype - DHCP message type (safely ignored)
serverid - The IP of the server
message - Reason for a DHCPNAK
tftp - The TFTP server name
bootfile - The bootfile name
additional options are easily added in options.c.
note on udhcpc's random seed
---------------------------
udhcpc will seed its random number generator (used for generating xid's)
by reading /dev/urandom. If you have a lot of embedded systems on the same
network, with no entropy, you can either seed /dev/urandom by a method of
your own, or doing the following on startup:
ifconfig eth0 > /dev/urandom
in order to seed /dev/urandom with some data (mac address) unique to your
system. If reading /dev/urandom fails, udhcpc will fall back to its old
behavior of seeding with time(0).
signals accepted by udhcpc
-------------------------
udhcpc also responds to SIGUSR1 and SIGUSR2. SIGUSR1 will force a renew state,
and SIGUSR2 will force a release of the current lease, and cause udhcpc to
go into an inactive state (until it is killed, or receives a SIGUSR1). You do
not need to sleep between sending signals, as signals received are processed
sequencially in the order they are received.
compile time options
-------------------
options.c contains a set of dhcp options for the client:
name[10]: The name of the option as it will appear in scripts
flags: The type of option, as well as if it will be requested
by the client (OPTION_REQ)
code: The DHCP code for this option
-249
View File
@@ -1,249 +0,0 @@
/* clientpacket.c
*
* Packet generation and dispatching functions for the DHCP client.
*
* Russ Dill <Russ.Dill@asu.edu> July 2001
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <string.h>
#include <sys/socket.h>
#include <features.h>
#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
#include <netpacket/packet.h>
#include <net/ethernet.h>
#else
#include <asm/types.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#endif
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include "dhcpd.h"
#include "clientpacket.h"
#include "options.h"
#include "dhcpc.h"
#include "common.h"
/* Create a random xid */
unsigned long random_xid(void)
{
static int initialized;
if (!initialized) {
int fd;
unsigned long seed;
fd = open("/dev/urandom", 0);
if (fd < 0 || read(fd, &seed, sizeof(seed)) < 0) {
LOG(LOG_WARNING, "Could not load seed from /dev/urandom: %m");
seed = time(0);
}
if (fd >= 0) close(fd);
srand(seed);
initialized++;
}
return rand();
}
/* initialize a packet with the proper defaults */
static void init_packet(struct dhcpMessage *packet, char type)
{
struct vendor {
char vendor, length;
char str[sizeof("udhcp "VERSION)];
} vendor_id = { DHCP_VENDOR, sizeof("udhcp "VERSION) - 1, "udhcp "VERSION};
init_header(packet, type);
memcpy(packet->chaddr, client_config.arp, 6);
add_option_string(packet->options, client_config.clientid);
if (client_config.hostname) add_option_string(packet->options, client_config.hostname);
if (client_config.fqdn) add_option_string(packet->options, client_config.fqdn);
add_option_string(packet->options, (uint8_t *) &vendor_id);
}
/* Add a parameter request list for stubborn DHCP servers. Pull the data
* from the struct in options.c. Don't do bounds checking here because it
* goes towards the head of the packet. */
static void add_requests(struct dhcpMessage *packet)
{
int end = end_option(packet->options);
int i, len = 0;
packet->options[end + OPT_CODE] = DHCP_PARAM_REQ;
for (i = 0; dhcp_options[i].code; i++)
if (dhcp_options[i].flags & OPTION_REQ)
packet->options[end + OPT_DATA + len++] = dhcp_options[i].code;
packet->options[end + OPT_LEN] = len;
packet->options[end + OPT_DATA + len] = DHCP_END;
}
/* Broadcast a DHCP discover packet to the network, with an optionally requested IP */
int send_discover(unsigned long xid, unsigned long requested)
{
struct dhcpMessage packet;
init_packet(&packet, DHCPDISCOVER);
packet.xid = xid;
if (requested)
add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
add_requests(&packet);
LOG(LOG_DEBUG, "Sending discover...");
return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
}
/* Broadcasts a DHCP request message */
int send_selecting(unsigned long xid, unsigned long server, unsigned long requested)
{
struct dhcpMessage packet;
struct in_addr addr;
init_packet(&packet, DHCPREQUEST);
packet.xid = xid;
add_simple_option(packet.options, DHCP_REQUESTED_IP, requested);
add_simple_option(packet.options, DHCP_SERVER_ID, server);
add_requests(&packet);
addr.s_addr = requested;
LOG(LOG_DEBUG, "Sending select for %s...", inet_ntoa(addr));
return raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
}
/* Unicasts or broadcasts a DHCP renew message */
int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr)
{
struct dhcpMessage packet;
int ret = 0;
init_packet(&packet, DHCPREQUEST);
packet.xid = xid;
packet.ciaddr = ciaddr;
add_requests(&packet);
LOG(LOG_DEBUG, "Sending renew...");
if (server)
ret = kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
else ret = raw_packet(&packet, INADDR_ANY, CLIENT_PORT, INADDR_BROADCAST,
SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex);
return ret;
}
/* Unicasts a DHCP release message */
int send_release(unsigned long server, unsigned long ciaddr)
{
struct dhcpMessage packet;
init_packet(&packet, DHCPRELEASE);
packet.xid = random_xid();
packet.ciaddr = ciaddr;
add_simple_option(packet.options, DHCP_REQUESTED_IP, ciaddr);
add_simple_option(packet.options, DHCP_SERVER_ID, server);
LOG(LOG_DEBUG, "Sending release...");
return kernel_packet(&packet, ciaddr, CLIENT_PORT, server, SERVER_PORT);
}
/* return -1 on errors that are fatal for the socket, -2 for those that aren't */
int get_raw_packet(struct dhcpMessage *payload, int fd)
{
int bytes;
struct udp_dhcp_packet packet;
uint32_t source, dest;
uint16_t check;
memset(&packet, 0, sizeof(struct udp_dhcp_packet));
bytes = read(fd, &packet, sizeof(struct udp_dhcp_packet));
if (bytes < 0) {
DEBUG(LOG_INFO, "couldn't read on raw listening socket -- ignoring");
usleep(500000); /* possible down interface, looping condition */
return -1;
}
if (bytes < (int) (sizeof(struct iphdr) + sizeof(struct udphdr))) {
DEBUG(LOG_INFO, "message too short, ignoring");
return -2;
}
if (bytes < ntohs(packet.ip.tot_len)) {
DEBUG(LOG_INFO, "Truncated packet");
return -2;
}
/* ignore any extra garbage bytes */
bytes = ntohs(packet.ip.tot_len);
/* Make sure its the right packet for us, and that it passes sanity checks */
if (packet.ip.protocol != IPPROTO_UDP || packet.ip.version != IPVERSION ||
packet.ip.ihl != sizeof(packet.ip) >> 2 || packet.udp.dest != htons(CLIENT_PORT) ||
bytes > (int) sizeof(struct udp_dhcp_packet) ||
ntohs(packet.udp.len) != (uint16_t) (bytes - sizeof(packet.ip))) {
DEBUG(LOG_INFO, "unrelated/bogus packet");
return -2;
}
/* check IP checksum */
check = packet.ip.check;
packet.ip.check = 0;
if (check != checksum(&(packet.ip), sizeof(packet.ip))) {
DEBUG(LOG_INFO, "bad IP header checksum, ignoring");
return -1;
}
/* verify the UDP checksum by replacing the header with a psuedo header */
source = packet.ip.saddr;
dest = packet.ip.daddr;
check = packet.udp.check;
packet.udp.check = 0;
memset(&packet.ip, 0, sizeof(packet.ip));
packet.ip.protocol = IPPROTO_UDP;
packet.ip.saddr = source;
packet.ip.daddr = dest;
packet.ip.tot_len = packet.udp.len; /* cheat on the psuedo-header */
if (check && check != checksum(&packet, bytes)) {
DEBUG(LOG_ERR, "packet with bad UDP checksum received, ignoring");
return -2;
}
memcpy(payload, &(packet.data), bytes - (sizeof(packet.ip) + sizeof(packet.udp)));
if (ntohl(payload->cookie) != DHCP_MAGIC) {
LOG(LOG_ERR, "received bogus message (bad magic) -- ignoring");
return -2;
}
DEBUG(LOG_INFO, "oooooh!!! got some!");
return bytes - (sizeof(packet.ip) + sizeof(packet.udp));
}
-14
View File
@@ -1,14 +0,0 @@
#ifndef _CLIENTPACKET_H
#define _CLIENTPACKET_H
#include "packet.h"
unsigned long random_xid(void);
int send_discover(unsigned long xid, unsigned long requested);
int send_selecting(unsigned long xid, unsigned long server, unsigned long requested);
int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr);
int send_renew(unsigned long xid, unsigned long server, unsigned long ciaddr);
int send_release(unsigned long server, unsigned long ciaddr);
int get_raw_packet(struct dhcpMessage *payload, int fd);
#endif
-62
View File
@@ -1,62 +0,0 @@
/*
* clientsocket.c -- DHCP client socket creation
*
* udhcp client
*
* Russ Dill <Russ.Dill@asu.edu> July 2001
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <features.h>
#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
#include <netpacket/packet.h>
#include <net/ethernet.h>
#else
#include <asm/types.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#endif
#include "clientsocket.h"
#include "common.h"
int raw_socket(int ifindex)
{
int fd;
struct sockaddr_ll sock;
DEBUG(LOG_INFO, "Opening raw socket on ifindex %d", ifindex);
if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
DEBUG(LOG_ERR, "socket call failed: %m");
return -1;
}
sock.sll_family = AF_PACKET;
sock.sll_protocol = htons(ETH_P_IP);
sock.sll_ifindex = ifindex;
if (bind(fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) {
DEBUG(LOG_ERR, "bind call failed: %m");
close(fd);
return -1;
}
return fd;
}
-7
View File
@@ -1,7 +0,0 @@
/* clientsocket.h */
#ifndef _CLIENTSOCKET_H
#define _CLIENTSOCKET_H
int raw_socket(int ifindex);
#endif
-162
View File
@@ -1,162 +0,0 @@
/* common.c
*
* Functions for debugging and logging as well as some other
* simple helper functions.
*
* Russ Dill <Russ.Dill@asu.edu> 2001-2003
* Rewritten by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <paths.h>
#include <sys/socket.h>
#include <stdarg.h>
#include "common.h"
#include "pidfile.h"
static int daemonized;
long uptime(void)
{
struct sysinfo info;
sysinfo(&info);
return info.uptime;
}
/*
* This function makes sure our first socket calls
* aren't going to fd 1 (printf badness...) and are
* not later closed by daemon()
*/
static inline void sanitize_fds(void)
{
int zero;
if ((zero = open(_PATH_DEVNULL, O_RDWR, 0)) < 0) return;
while (zero < 3) zero = dup(zero);
close(zero);
}
void background(const char *pidfile)
{
#ifdef __uClinux__
LOG(LOG_ERR, "Cannot background in uclinux (yet)");
#else /* __uClinux__ */
int pid_fd;
/* hold lock during fork. */
pid_fd = pidfile_acquire(pidfile);
if (daemon(0, 0) == -1) {
perror("fork");
exit(1);
}
daemonized++;
pidfile_write_release(pid_fd);
#endif /* __uClinux__ */
}
#ifdef UDHCP_SYSLOG
void udhcp_logging(int level, const char *fmt, ...)
{
va_list p;
va_list p2;
va_start(p, fmt);
__va_copy(p2, p);
if(!daemonized) {
vprintf(fmt, p);
putchar('\n');
}
vsyslog(level, fmt, p2);
va_end(p);
}
void start_log_and_pid(const char *client_server, const char *pidfile)
{
int pid_fd;
/* Make sure our syslog fd isn't overwritten */
sanitize_fds();
/* do some other misc startup stuff while we are here to save bytes */
pid_fd = pidfile_acquire(pidfile);
pidfile_write_release(pid_fd);
/* equivelent of doing a fflush after every \n */
setlinebuf(stdout);
openlog(client_server, LOG_PID | LOG_CONS, LOG_LOCAL0);
udhcp_logging(LOG_INFO, "%s (v%s) started", client_server, VERSION);
}
#else
static char *syslog_level_msg[] = {
[LOG_EMERG] = "EMERGENCY!",
[LOG_ALERT] = "ALERT!",
[LOG_CRIT] = "critical!",
[LOG_WARNING] = "warning",
[LOG_ERR] = "error",
[LOG_INFO] = "info",
[LOG_DEBUG] = "debug"
};
void udhcp_logging(int level, const char *fmt, ...)
{
va_list p;
va_start(p, fmt);
if(!daemonized) {
printf("%s, ", syslog_level_msg[level]);
vprintf(fmt, p);
putchar('\n');
}
va_end(p);
}
void start_log_and_pid(const char *client_server, const char *pidfile)
{
int pid_fd;
/* Make sure our syslog fd isn't overwritten */
sanitize_fds();
/* do some other misc startup stuff while we are here to save bytes */
pid_fd = pidfile_acquire(pidfile);
pidfile_write_release(pid_fd);
/* equivelent of doing a fflush after every \n */
setlinebuf(stdout);
udhcp_logging(LOG_INFO, "%s (v%s) started", client_server, VERSION);
}
#endif
-56
View File
@@ -1,56 +0,0 @@
/* common.h
*
* Russ Dill <Russ.Dill@asu.edu> September 2001
* Rewritten by Vladimir Oleynik <dzo@simtreas.ru> (C) 2003
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _COMMON_H
#define _COMMON_H
#include "version.h"
#include "libbb_udhcp.h"
#ifndef UDHCP_SYSLOG
enum syslog_levels {
LOG_EMERG = 0,
LOG_ALERT,
LOG_CRIT,
LOG_WARNING,
LOG_ERR,
LOG_INFO,
LOG_DEBUG
};
#else
#include <syslog.h>
#endif
long uptime(void);
void background(const char *pidfile);
void start_log_and_pid(const char *client_server, const char *pidfile);
void background(const char *pidfile);
void udhcp_logging(int level, const char *fmt, ...);
#define LOG(level, str, args...) udhcp_logging(level, str, ## args)
#ifdef UDHCP_DEBUG
# define DEBUG(level, str, args...) LOG(level, str, ## args)
#else
# define DEBUG(level, str, args...) do {;} while(0)
#endif
#endif
+467
View File
@@ -0,0 +1,467 @@
#define _DEFAULT_SOURCE
#include "dhcp.h"
#include <arpa/inet.h>
#include <errno.h>
#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <net/if.h>
#include <netinet/ip.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#define DHCP_CLIENT_PORT 68
#define DHCP_SERVER_PORT 67
#define DHCPDISCOVER 1
#define DHCPOFFER 2
#define DHCPREQUEST 3
#define DHCPACK 5
#define DHCPNAK 6
#define DHCP_MAGIC_COOKIE 0x63825363u
#define DHCP_FLAGS_BROADCAST 0x8000
#define DHCP_OPTION_SUBNET_MASK 1
#define DHCP_OPTION_ROUTER 3
#define DHCP_OPTION_DNS 6
#define DHCP_OPTION_REQUESTED_IP 50
#define DHCP_OPTION_LEASE_TIME 51
#define DHCP_OPTION_MSG_TYPE 53
#define DHCP_OPTION_SERVER_IDENTIFIER 54
#define DHCP_OPTION_PARAMETER_REQUEST 55
#define DHCP_OPTION_END 255
#define DHCP_OPTION_PAD 0
#define DHCP_HTYPE_ETHERNET 1
#define DHCP_HLEN_ETHERNET 6
#define DHCP_OP_BOOTREQUEST 1
#define DHCP_OP_BOOTREPLY 2
#define DHCP_OPTIONS_SIZE 312
#define DHCP_MIN_SIZE 300
#define PKT_BUF_SIZE 1500
#pragma pack(push, 1)
typedef struct {
uint8_t op;
uint8_t htype;
uint8_t hlen;
uint8_t hops;
uint32_t xid;
uint16_t secs;
uint16_t flags;
uint32_t ciaddr;
uint32_t yiaddr;
uint32_t siaddr;
uint32_t giaddr;
uint8_t chaddr[16];
uint8_t sname[64];
uint8_t file[128];
uint32_t magic_cookie;
uint8_t options[DHCP_OPTIONS_SIZE];
} dhcp_packet_t;
typedef struct {
uint16_t src;
uint16_t dst;
uint16_t len;
uint16_t check;
} sdhcp_udp_header_t;
#pragma pack(pop)
static uint16_t ip_checksum(const void *data, size_t len)
{
const uint8_t *p = data;
uint32_t sum = 0;
while (len > 1) {
sum += ((uint16_t)p[0] << 8) | p[1];
p += 2;
len -= 2;
}
if (len) {
sum += ((uint16_t)p[0] << 8);
}
while (sum >> 16) {
sum = (sum & 0xffff) + (sum >> 16);
}
return (uint16_t)~sum;
}
static uint32_t make_xid(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint32_t)ts.tv_nsec ^ (uint32_t)ts.tv_sec ^ (uint32_t)getpid();
}
static int get_mac(const char *ifname, uint8_t mac[6])
{
int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0) return -1;
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1);
if (ioctl(fd, SIOCGIFHWADDR, &ifr) < 0) {
close(fd);
return -1;
}
memcpy(mac, ifr.ifr_hwaddr.sa_data, 6);
close(fd);
return 0;
}
static int open_sock(const char *ifname, int *ifindex_out)
{
int ifindex = if_nametoindex(ifname);
if (!ifindex) {
errno = ENODEV;
return -1;
}
int fd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
if (fd < 0) return -1;
struct sockaddr_ll addr;
memset(&addr, 0, sizeof(addr));
addr.sll_family = AF_PACKET;
addr.sll_protocol = htons(ETH_P_IP);
addr.sll_ifindex = ifindex;
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
close(fd);
return -1;
}
*ifindex_out = ifindex;
return fd;
}
static size_t dhcp_init(dhcp_packet_t *d, uint32_t xid, const uint8_t mac[6], uint8_t msg_type)
{
memset(d, 0, sizeof(*d));
d->op = DHCP_OP_BOOTREQUEST;
d->htype = DHCP_HTYPE_ETHERNET;
d->hlen = DHCP_HLEN_ETHERNET;
d->xid = htonl(xid);
d->flags = htons(DHCP_FLAGS_BROADCAST);
memcpy(d->chaddr, mac, 6);
d->magic_cookie = htonl(DHCP_MAGIC_COOKIE);
size_t o = 0;
d->options[o++] = DHCP_OPTION_MSG_TYPE;
d->options[o++] = 1;
d->options[o++] = msg_type;
return o;
}
static size_t build_discover(dhcp_packet_t *d, uint32_t xid, const uint8_t mac[6])
{
size_t o = dhcp_init(d, xid, mac, DHCPDISCOVER);
uint8_t prl[] = {
DHCP_OPTION_SUBNET_MASK,
DHCP_OPTION_ROUTER,
DHCP_OPTION_DNS,
DHCP_OPTION_LEASE_TIME,
DHCP_OPTION_SERVER_IDENTIFIER
};
d->options[o++] = DHCP_OPTION_PARAMETER_REQUEST;
d->options[o++] = sizeof(prl);
memcpy(&d->options[o], prl, sizeof(prl));
o += sizeof(prl);
d->options[o++] = DHCP_OPTION_END;
size_t len = offsetof(dhcp_packet_t, options) + o;
return len < DHCP_MIN_SIZE ? DHCP_MIN_SIZE : len;
}
static size_t build_request(dhcp_packet_t *d, uint32_t xid, const uint8_t mac[6],
uint32_t req_ip, uint32_t server_id)
{
size_t o = dhcp_init(d, xid, mac, DHCPREQUEST);
d->options[o++] = DHCP_OPTION_REQUESTED_IP;
d->options[o++] = 4;
memcpy(&d->options[o], &req_ip, 4);
o += 4;
d->options[o++] = DHCP_OPTION_SERVER_IDENTIFIER;
d->options[o++] = 4;
memcpy(&d->options[o], &server_id, 4);
o += 4;
uint8_t prl[] = {
DHCP_OPTION_SUBNET_MASK,
DHCP_OPTION_ROUTER,
DHCP_OPTION_DNS,
DHCP_OPTION_LEASE_TIME
};
d->options[o++] = DHCP_OPTION_PARAMETER_REQUEST;
d->options[o++] = sizeof(prl);
memcpy(&d->options[o], prl, sizeof(prl));
o += sizeof(prl);
d->options[o++] = DHCP_OPTION_END;
size_t len = offsetof(dhcp_packet_t, options) + o;
return len < DHCP_MIN_SIZE ? DHCP_MIN_SIZE : len;
}
static int build_ip_udp(uint8_t *buf, size_t buflen,
const dhcp_packet_t *dhcp, size_t dhcp_len,
size_t *out_len)
{
size_t total = sizeof(struct iphdr) + sizeof(sdhcp_udp_header_t) + dhcp_len;
if (buflen < total) {
errno = EMSGSIZE;
return -1;
}
struct iphdr *ip = (struct iphdr *)buf;
sdhcp_udp_header_t *udp = (sdhcp_udp_header_t *)(buf + sizeof(struct iphdr));
uint8_t *payload = buf + sizeof(struct iphdr) + sizeof(sdhcp_udp_header_t);
memset(buf, 0, sizeof(struct iphdr) + sizeof(sdhcp_udp_header_t));
memcpy(payload, dhcp, dhcp_len);
ip->version = 4;
ip->ihl = 5;
ip->ttl = 64;
ip->protocol = IPPROTO_UDP;
ip->tot_len = htons(total);
ip->saddr = htonl(INADDR_ANY);
ip->daddr = htonl(INADDR_BROADCAST);
ip->check = htons(ip_checksum(ip, sizeof(*ip)));
udp->src = htons(DHCP_CLIENT_PORT);
udp->dst = htons(DHCP_SERVER_PORT);
udp->len = htons(sizeof(sdhcp_udp_header_t) + dhcp_len);
udp->check = 0; /* IPv4 allows zero UDP checksum */
*out_len = total;
return 0;
}
static int send_buf(int fd, int ifindex, const void *buf, size_t len)
{
struct sockaddr_ll dst;
memset(&dst, 0, sizeof(dst));
dst.sll_family = AF_PACKET;
dst.sll_protocol = htons(ETH_P_IP);
dst.sll_ifindex = ifindex;
dst.sll_halen = ETH_ALEN;
memset(dst.sll_addr, 0xff, ETH_ALEN);
ssize_t n = sendto(fd, buf, len, 0, (struct sockaddr *)&dst, sizeof(dst));
return n == (ssize_t)len ? 0 : -1;
}
static int parse_options(const dhcp_packet_t *d, size_t len,
uint8_t *msg_type, sdhcp_lease_t *lease)
{
size_t base = offsetof(dhcp_packet_t, options);
if (len < base) return -1;
if (ntohl(d->magic_cookie) != DHCP_MAGIC_COOKIE) return -1;
*msg_type = 0;
lease->subnet_mask.s_addr = 0;
lease->router.s_addr = 0;
lease->dns_count = 0;
lease->lease_time = 0;
lease->server_identifier = 0;
size_t i = 0, max = len - base;
while (i < max) {
uint8_t code = d->options[i++];
if (code == DHCP_OPTION_PAD) continue;
if (code == DHCP_OPTION_END) break;
if (i >= max) return -1;
uint8_t optlen = d->options[i++];
if ((size_t)optlen > max - i) return -1;
const uint8_t *v = &d->options[i];
switch (code) {
case DHCP_OPTION_MSG_TYPE:
if (optlen == 1) *msg_type = v[0];
break;
case DHCP_OPTION_SUBNET_MASK:
if (optlen == 4) memcpy(&lease->subnet_mask.s_addr, v, 4);
break;
case DHCP_OPTION_ROUTER:
if (optlen >= 4) memcpy(&lease->router.s_addr, v, 4);
break;
case DHCP_OPTION_DNS: {
int count = optlen / 4;
if (count > SDHCP_MAX_DNS) count = SDHCP_MAX_DNS;
for (int j = 0; j < count; j++) {
memcpy(&lease->dns[j].s_addr, v + j * 4, 4);
}
lease->dns_count = count;
break;
}
case DHCP_OPTION_LEASE_TIME:
if (optlen == 4) {
uint32_t t;
memcpy(&t, v, 4);
lease->lease_time = ntohl(t);
}
break;
case DHCP_OPTION_SERVER_IDENTIFIER:
if (optlen == 4) memcpy(&lease->server_identifier, v, 4);
break;
}
i += optlen;
}
return 0;
}
static int recv_dhcp(int fd, uint32_t xid, const uint8_t mac[6], int timeout_ms,
dhcp_packet_t *out, size_t *out_len, uint8_t *msg_type,
sdhcp_lease_t *lease)
{
uint8_t buf[PKT_BUF_SIZE];
struct timeval start, now;
gettimeofday(&start, NULL);
for (;;) {
gettimeofday(&now, NULL);
long elapsed = (now.tv_sec - start.tv_sec) * 1000L +
(now.tv_usec - start.tv_usec) / 1000L;
long remain = timeout_ms - elapsed;
if (remain <= 0) {
errno = ETIMEDOUT;
return -1;
}
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
struct timeval tv;
tv.tv_sec = remain / 1000;
tv.tv_usec = (remain % 1000) * 1000;
int rc = select(fd + 1, &rfds, NULL, NULL, &tv);
if (rc < 0) return -1;
if (rc == 0) {
errno = ETIMEDOUT;
return -1;
}
ssize_t n = recv(fd, buf, sizeof(buf), 0);
if (n < (ssize_t)(sizeof(struct iphdr) + sizeof(sdhcp_udp_header_t))) continue;
struct iphdr *ip = (struct iphdr *)buf;
if (ip->version != 4 || ip->ihl < 5 || ip->protocol != IPPROTO_UDP) continue;
size_t ip_hlen = ip->ihl * 4;
if ((size_t)n < ip_hlen + sizeof(sdhcp_udp_header_t)) continue;
sdhcp_udp_header_t *udp = (sdhcp_udp_header_t *)(buf + ip_hlen);
if (ntohs(udp->src) != DHCP_SERVER_PORT || ntohs(udp->dst) != DHCP_CLIENT_PORT) continue;
uint16_t udp_len = ntohs(udp->len);
if (udp_len < sizeof(sdhcp_udp_header_t)) continue;
size_t dhcp_len = udp_len - sizeof(sdhcp_udp_header_t);
if (dhcp_len < offsetof(dhcp_packet_t, options)) continue;
if (dhcp_len > sizeof(*out)) continue;
if ((size_t)n < ip_hlen + sizeof(sdhcp_udp_header_t) + dhcp_len) continue;
const dhcp_packet_t *d = (const dhcp_packet_t *)(buf + ip_hlen + sizeof(sdhcp_udp_header_t));
if (d->op != DHCP_OP_BOOTREPLY) continue;
if (d->htype != DHCP_HTYPE_ETHERNET || d->hlen != DHCP_HLEN_ETHERNET) continue;
if (ntohl(d->xid) != xid) continue;
if (memcmp(d->chaddr, mac, 6) != 0) continue;
lease->yiaddr.s_addr = d->yiaddr;
if (parse_options(d, dhcp_len, msg_type, lease) < 0) continue;
memcpy(out, d, dhcp_len);
*out_len = dhcp_len;
return 0;
}
}
int sdhcp_acquire(const char *ifname, int timeout_ms, int retries, sdhcp_lease_t *lease)
{
if (!ifname || !lease || timeout_ms <= 0 || retries <= 0) {
errno = EINVAL;
return -1;
}
memset(lease, 0, sizeof(*lease));
uint8_t mac[6];
if (get_mac(ifname, mac) < 0) return -1;
int ifindex;
int fd = open_sock(ifname, &ifindex);
if (fd < 0) return -1;
uint32_t xid = make_xid();
dhcp_packet_t tx, rx;
uint8_t buf[PKT_BUF_SIZE];
size_t tx_len, rx_len;
uint8_t msg_type;
for (int attempt = 0; attempt < retries; attempt++) {
size_t dhcp_len = build_discover(&tx, xid, mac);
if (build_ip_udp(buf, sizeof(buf), &tx, dhcp_len, &tx_len) < 0) break;
if (send_buf(fd, ifindex, buf, tx_len) < 0) continue;
if (recv_dhcp(fd, xid, mac, timeout_ms, &rx, &rx_len, &msg_type, lease) < 0) {
continue;
}
if (msg_type != DHCPOFFER) continue;
if (!rx.yiaddr || !lease->server_identifier) continue;
dhcp_len = build_request(&tx, xid, mac, rx.yiaddr, lease->server_identifier);
if (build_ip_udp(buf, sizeof(buf), &tx, dhcp_len, &tx_len) < 0) break;
if (send_buf(fd, ifindex, buf, tx_len) < 0) continue;
if (recv_dhcp(fd, xid, mac, timeout_ms, &rx, &rx_len, &msg_type, lease) < 0) {
continue;
}
if (msg_type == DHCPNAK) {
errno = EPROTO;
close(fd);
return -1;
}
if (msg_type == DHCPACK) {
close(fd);
return 0;
}
}
close(fd);
errno = ETIMEDOUT;
return -1;
}
+24
View File
@@ -0,0 +1,24 @@
#ifndef DHCP_H
#define DHCP_H
#include <stdint.h>
#include <netinet/in.h>
#define SDHCP_MAX_DNS 4
typedef struct {
struct in_addr yiaddr;
struct in_addr subnet_mask;
struct in_addr router;
struct in_addr dns[SDHCP_MAX_DNS];
int dns_count;
uint32_t lease_time; /* host byte order */
uint32_t server_identifier; /* network byte order */
} sdhcp_lease_t;
// Simple one-shot DHCP acquisition for connection to Wii U
//
// Return 0 on success, -1 on error with errno set
int sdhcp_acquire(const char *ifname, int timeout_ms, int retries, sdhcp_lease_t *lease);
#endif
-535
View File
@@ -1,535 +0,0 @@
/* dhcpc.c
*
* udhcp DHCP client
*
* Russ Dill <Russ.Dill@asu.edu> July 2001
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <sys/time.h>
#include <sys/file.h>
#include <unistd.h>
#include <getopt.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <time.h>
#include <string.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <errno.h>
#include "dhcpd.h"
#include "dhcpc.h"
#include "options.h"
#include "clientpacket.h"
#include "clientsocket.h"
#include "script.h"
#include "socket.h"
#include "common.h"
#include "signalpipe.h"
static int state;
static unsigned long requested_ip; /* = 0 */
static unsigned long server_addr;
static unsigned long timeout;
static int packet_num; /* = 0 */
static int fd = -1;
#define LISTEN_NONE 0
#define LISTEN_KERNEL 1
#define LISTEN_RAW 2
static int listen_mode;
struct client_config_t client_config = {
/* Default options. */
abort_if_no_lease: 0,
foreground: 0,
quit_after_lease: 0,
background_if_no_lease: 0,
interface: "eth0",
pidfile: NULL,
callback: NULL,
clientid: NULL,
hostname: NULL,
fqdn: NULL,
ifindex: 0,
arp: "\0\0\0\0\0\0", /* appease gcc-3.0 */
};
#ifndef IN_BUSYBOX
static void __attribute__ ((noreturn)) show_usage(void)
{
printf(
"Usage: udhcpc [OPTIONS]\n\n"
" -c, --clientid=CLIENTID Client identifier\n"
" -H, --hostname=HOSTNAME Client hostname\n"
" -h Alias for -H\n"
" -F, --fqdn=FQDN Client fully qualified domain name\n"
" -f, --foreground Do not fork after getting lease\n"
" -b, --background Fork to background if lease cannot be\n"
" immediately negotiated.\n"
" -i, --interface=INTERFACE Interface to use (default: eth0)\n"
" -n, --now Exit with failure if lease cannot be\n"
" immediately negotiated.\n"
" -p, --pidfile=file Store process ID of daemon in file\n"
" -q, --quit Quit after obtaining lease\n"
" -r, --request=IP IP address to request (default: none)\n"
" -s, --script=file Run file at dhcp events (default:\n"
" " DEFAULT_SCRIPT ")\n"
" -v, --version Display version\n"
);
exit(0);
}
#else
#define show_usage bb_show_usage
extern void show_usage(void) __attribute__ ((noreturn));
#endif
/* just a little helper */
static void change_mode(int new_mode)
{
DEBUG(LOG_INFO, "entering %s listen mode",
new_mode ? (new_mode == 1 ? "kernel" : "raw") : "none");
if (fd >= 0) close(fd);
fd = -1;
listen_mode = new_mode;
}
/* perform a renew */
static void perform_renew(void)
{
LOG(LOG_INFO, "Performing a DHCP renew");
switch (state) {
case BOUND:
change_mode(LISTEN_KERNEL);
case RENEWING:
case REBINDING:
state = RENEW_REQUESTED;
break;
case RENEW_REQUESTED: /* impatient are we? fine, square 1 */
run_script(NULL, "deconfig");
case REQUESTING:
case RELEASED:
change_mode(LISTEN_RAW);
state = INIT_SELECTING;
break;
case INIT_SELECTING:
break;
}
/* start things over */
packet_num = 0;
/* Kill any timeouts because the user wants this to hurry along */
timeout = 0;
}
/* perform a release */
static void perform_release(void)
{
char buffer[16];
struct in_addr temp_addr;
/* send release packet */
if (state == BOUND || state == RENEWING || state == REBINDING) {
temp_addr.s_addr = server_addr;
sprintf(buffer, "%s", inet_ntoa(temp_addr));
temp_addr.s_addr = requested_ip;
LOG(LOG_INFO, "Unicasting a release of %s to %s",
inet_ntoa(temp_addr), buffer);
send_release(server_addr, requested_ip); /* unicast */
run_script(NULL, "deconfig");
}
LOG(LOG_INFO, "Entering released state");
change_mode(LISTEN_NONE);
state = RELEASED;
timeout = 0x7fffffff;
}
static void client_background(void)
{
background(client_config.pidfile);
client_config.foreground = 1; /* Do not fork again. */
client_config.background_if_no_lease = 0;
}
int udhcpc_main()
{
uint8_t *temp, *message;
unsigned long t1 = 0, t2 = 0, xid = 0;
unsigned long start = 0, lease;
fd_set rfds;
int retval;
struct timeval tv;
int c, len;
struct dhcpMessage packet;
struct in_addr temp_addr;
long now;
int max_fd;
int sig;
static const struct option arg_options[] = {
{"clientid", required_argument, 0, 'c'},
{"foreground", no_argument, 0, 'f'},
{"background", no_argument, 0, 'b'},
{"hostname", required_argument, 0, 'H'},
{"hostname", required_argument, 0, 'h'},
{"fqdn", required_argument, 0, 'F'},
{"interface", required_argument, 0, 'i'},
{"now", no_argument, 0, 'n'},
{"pidfile", required_argument, 0, 'p'},
{"quit", no_argument, 0, 'q'},
{"request", required_argument, 0, 'r'},
{"script", required_argument, 0, 's'},
{"version", no_argument, 0, 'v'},
{0, 0, 0, 0}
};
/* get options */
// while (1) {
// int option_index = 0;
// c = getopt_long(argc, argv, "c:fbH:h:F:i:np:qr:s:v", arg_options, &option_index);
// if (c == -1) break;
// switch (c) {
// case 'c':
// len = strlen(optarg) > 255 ? 255 : strlen(optarg);
// if (client_config.clientid) free(client_config.clientid);
// client_config.clientid = xmalloc(len + 2);
// client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID;
// client_config.clientid[OPT_LEN] = len;
// client_config.clientid[OPT_DATA] = '\0';
// strncpy(client_config.clientid + OPT_DATA, optarg, len);
// break;
// case 'f':
// client_config.foreground = 1;
// break;
// case 'b':
// client_config.background_if_no_lease = 1;
// break;
// case 'h':
// case 'H':
// len = strlen(optarg) > 255 ? 255 : strlen(optarg);
// if (client_config.hostname) free(client_config.hostname);
// client_config.hostname = xmalloc(len + 2);
// client_config.hostname[OPT_CODE] = DHCP_HOST_NAME;
// client_config.hostname[OPT_LEN] = len;
// strncpy(client_config.hostname + 2, optarg, len);
// break;
// case 'F':
// len = strlen(optarg) > 255 ? 255 : strlen(optarg);
// if (client_config.fqdn) free(client_config.fqdn);
// client_config.fqdn = xmalloc(len + 5);
// client_config.fqdn[OPT_CODE] = DHCP_FQDN;
// client_config.fqdn[OPT_LEN] = len + 3;
// /* Flags: 0000NEOS
// S: 1 => Client requests Server to update A RR in DNS as well as PTR
// O: 1 => Server indicates to client that DNS has been updated regardless
// E: 1 => Name data is DNS format, i.e. <4>host<6>domain<4>com<0> not "host.domain.com"
// N: 1 => Client requests Server to not update DNS
// */
// client_config.fqdn[OPT_LEN + 1] = 0x1;
// client_config.fqdn[OPT_LEN + 2] = 0;
// client_config.fqdn[OPT_LEN + 3] = 0;
// strncpy(client_config.fqdn + 5, optarg, len);
// break;
// case 'i':
// client_config.interface = optarg;
// break;
// case 'n':
// client_config.abort_if_no_lease = 1;
// break;
// case 'p':
// client_config.pidfile = optarg;
// break;
// case 'q':
// client_config.quit_after_lease = 1;
// break;
// case 'r':
// requested_ip = inet_addr(optarg);
// break;
// case 's':
// client_config.script = optarg;
// break;
// case 'v':
// printf("udhcpcd, version %s\n\n", VERSION);
// return 0;
// break;
// default:
// show_usage();
// }
// }
/* Start the log, sanitize fd's, and write a pid file */
start_log_and_pid("udhcpc", client_config.pidfile);
if (read_interface(client_config.interface, &client_config.ifindex,
NULL, client_config.arp) < 0)
return 1;
if (!client_config.clientid) {
client_config.clientid = xmalloc(6 + 3);
client_config.clientid[OPT_CODE] = DHCP_CLIENT_ID;
client_config.clientid[OPT_LEN] = 7;
client_config.clientid[OPT_DATA] = 1;
memcpy(client_config.clientid + 3, client_config.arp, 6);
}
/* setup the signal pipe */
udhcp_sp_setup();
state = INIT_SELECTING;
run_script(NULL, "deconfig");
change_mode(LISTEN_RAW);
timeout = 0;
for (;;) {
tv.tv_sec = timeout - uptime();
tv.tv_usec = 0;
if (listen_mode != LISTEN_NONE && fd < 0) {
if (listen_mode == LISTEN_KERNEL)
fd = listen_socket(INADDR_ANY, CLIENT_PORT, client_config.interface);
else
fd = raw_socket(client_config.ifindex);
if (fd < 0) {
LOG(LOG_ERR, "FATAL: couldn't listen on socket, %m");
return 0;
}
}
max_fd = udhcp_sp_fd_set(&rfds, fd);
if (tv.tv_sec > 0) {
DEBUG(LOG_INFO, "Waiting on select...");
retval = select(max_fd + 1, &rfds, NULL, NULL, &tv);
} else retval = 0; /* If we already timed out, fall through */
now = uptime();
if (retval == 0) {
/* timeout dropped to zero */
switch (state) {
case INIT_SELECTING:
if (packet_num < 3) {
if (packet_num == 0)
xid = random_xid();
/* send discover packet */
send_discover(xid, requested_ip); /* broadcast */
timeout = now + ((packet_num == 2) ? 4 : 2);
packet_num++;
} else {
run_script(NULL, "leasefail");
if (client_config.background_if_no_lease) {
LOG(LOG_INFO, "No lease, forking to background.");
client_background();
} else if (client_config.abort_if_no_lease) {
LOG(LOG_INFO, "No lease, failing.");
return 1;
}
/* wait to try again */
packet_num = 0;
timeout = now + 60;
}
break;
case RENEW_REQUESTED:
case REQUESTING:
if (packet_num < 3) {
/* send request packet */
if (state == RENEW_REQUESTED)
send_renew(xid, server_addr, requested_ip); /* unicast */
else send_selecting(xid, server_addr, requested_ip); /* broadcast */
timeout = now + ((packet_num == 2) ? 10 : 2);
packet_num++;
} else {
/* timed out, go back to init state */
if (state == RENEW_REQUESTED) run_script(NULL, "deconfig");
state = INIT_SELECTING;
timeout = now;
packet_num = 0;
change_mode(LISTEN_RAW);
}
break;
case BOUND:
/* Lease is starting to run out, time to enter renewing state */
state = RENEWING;
change_mode(LISTEN_KERNEL);
DEBUG(LOG_INFO, "Entering renew state");
/* fall right through */
case RENEWING:
/* Either set a new T1, or enter REBINDING state */
if ((t2 - t1) <= (lease / 14400 + 1)) {
/* timed out, enter rebinding state */
state = REBINDING;
timeout = now + (t2 - t1);
DEBUG(LOG_INFO, "Entering rebinding state");
} else {
/* send a request packet */
send_renew(xid, server_addr, requested_ip); /* unicast */
t1 = (t2 - t1) / 2 + t1;
timeout = t1 + start;
}
break;
case REBINDING:
/* Either set a new T2, or enter INIT state */
if ((lease - t2) <= (lease / 14400 + 1)) {
/* timed out, enter init state */
state = INIT_SELECTING;
LOG(LOG_INFO, "Lease lost, entering init state");
run_script(NULL, "deconfig");
timeout = now;
packet_num = 0;
change_mode(LISTEN_RAW);
} else {
/* send a request packet */
send_renew(xid, 0, requested_ip); /* broadcast */
t2 = (lease - t2) / 2 + t2;
timeout = t2 + start;
}
break;
case RELEASED:
/* yah, I know, *you* say it would never happen */
timeout = 0x7fffffff;
break;
}
} else if (retval > 0 && listen_mode != LISTEN_NONE && FD_ISSET(fd, &rfds)) {
/* a packet is ready, read it */
if (listen_mode == LISTEN_KERNEL)
len = get_packet(&packet, fd);
else len = get_raw_packet(&packet, fd);
if (len == -1 && errno != EINTR) {
DEBUG(LOG_INFO, "error on read, %m, reopening socket");
change_mode(listen_mode); /* just close and reopen */
}
if (len < 0) continue;
if (packet.xid != xid) {
DEBUG(LOG_INFO, "Ignoring XID %lx (our xid is %lx)",
(unsigned long) packet.xid, xid);
continue;
}
if ((message = get_option(&packet, DHCP_MESSAGE_TYPE)) == NULL) {
DEBUG(LOG_ERR, "couldnt get option from packet -- ignoring");
continue;
}
switch (state) {
case INIT_SELECTING:
/* Must be a DHCPOFFER to one of our xid's */
if (*message == DHCPOFFER) {
if ((temp = get_option(&packet, DHCP_SERVER_ID))) {
memcpy(&server_addr, temp, 4);
xid = packet.xid;
requested_ip = packet.yiaddr;
/* enter requesting state */
state = REQUESTING;
timeout = now;
packet_num = 0;
} else {
DEBUG(LOG_ERR, "No server ID in message");
}
}
break;
case RENEW_REQUESTED:
case REQUESTING:
case RENEWING:
case REBINDING:
if (*message == DHCPACK) {
if (!(temp = get_option(&packet, DHCP_LEASE_TIME))) {
LOG(LOG_ERR, "No lease time with ACK, using 1 hour lease");
lease = 60 * 60;
} else {
memcpy(&lease, temp, 4);
lease = ntohl(lease);
}
/* enter bound state */
t1 = lease / 2;
/* little fixed point for n * .875 */
t2 = (lease * 0x7) >> 3;
temp_addr.s_addr = packet.yiaddr;
LOG(LOG_INFO, "Lease of %s obtained, lease time %ld",
inet_ntoa(temp_addr), lease);
start = now;
timeout = t1 + start;
requested_ip = packet.yiaddr;
run_script(&packet,
((state == RENEWING || state == REBINDING) ? "renew" : "bound"));
state = BOUND;
change_mode(LISTEN_NONE);
if (client_config.quit_after_lease)
return 0;
if (!client_config.foreground)
client_background();
} else if (*message == DHCPNAK) {
/* return to init state */
LOG(LOG_INFO, "Received DHCP NAK");
run_script(&packet, "nak");
if (state != REQUESTING)
run_script(NULL, "deconfig");
state = INIT_SELECTING;
timeout = now;
requested_ip = 0;
packet_num = 0;
change_mode(LISTEN_RAW);
sleep(3); /* avoid excessive network traffic */
}
break;
/* case BOUND, RELEASED: - ignore all packets */
}
} else if (retval > 0 && (sig = udhcp_sp_read(&rfds))) {
switch (sig) {
case SIGUSR1:
perform_renew();
break;
case SIGUSR2:
perform_release();
break;
case SIGTERM:
LOG(LOG_INFO, "Received SIGTERM");
return 0;
}
} else if (retval == -1 && errno == EINTR) {
/* a signal was caught */
} else {
/* An error occured */
DEBUG(LOG_ERR, "Error on select");
}
}
return 0;
}
-41
View File
@@ -1,41 +0,0 @@
/* dhcpc.h */
#ifndef _DHCPC_H
#define _DHCPC_H
#define DEFAULT_SCRIPT "/etc/udhcpc/default.script"
/* allow libbb_udhcp.h to redefine DEFAULT_SCRIPT */
#include "libbb_udhcp.h"
#define INIT_SELECTING 0
#define REQUESTING 1
#define BOUND 2
#define RENEWING 3
#define REBINDING 4
#define INIT_REBOOT 5
#define RENEW_REQUESTED 6
#define RELEASED 7
typedef void(*udhcpc_callback_t)(const char *name, char **envp, void *data);
struct client_config_t {
char foreground; /* Do not fork */
char quit_after_lease; /* Quit after obtaining lease */
char abort_if_no_lease; /* Abort if no lease */
char background_if_no_lease; /* Fork to background if no lease */
char *interface; /* The name of the interface to use */
char *pidfile; /* Optionally store the process ID */
udhcpc_callback_t callback; /* User callback to run at dhcp events */
void *callback_data; /* User callback to run at dhcp events */
uint8_t *clientid; /* Optional client id to use */
uint8_t *hostname; /* Optional hostname to use */
uint8_t *fqdn; /* Optional fully qualified domain name to use */
int ifindex; /* Index number of the interface to use */
uint8_t arp[6]; /* Our arp address */
};
extern struct client_config_t client_config;
extern int udhcpc_main();
#endif
-141
View File
@@ -1,141 +0,0 @@
/* dhcpd.h */
#ifndef _DHCPD_H
#define _DHCPD_H
#include <netinet/ip.h>
#include <netinet/udp.h>
#include "libbb_udhcp.h"
#include "leases.h"
#include "version.h"
/************************************/
/* Defaults _you_ may want to tweak */
/************************************/
/* the period of time the client is allowed to use that address */
#define LEASE_TIME (60*60*24*10) /* 10 days of seconds */
#define LEASES_FILE "/var/lib/misc/udhcpd.leases"
/* where to find the DHCP server configuration file */
#define DHCPD_CONF_FILE "/etc/udhcpd.conf"
/*****************************************************************/
/* Do not modify below here unless you know what you are doing!! */
/*****************************************************************/
/* DHCP protocol -- see RFC 2131 */
#define SERVER_PORT 67
#define CLIENT_PORT 68
#define DHCP_MAGIC 0x63825363
/* DHCP option codes (partial list) */
#define DHCP_PADDING 0x00
#define DHCP_SUBNET 0x01
#define DHCP_TIME_OFFSET 0x02
#define DHCP_ROUTER 0x03
#define DHCP_TIME_SERVER 0x04
#define DHCP_NAME_SERVER 0x05
#define DHCP_DNS_SERVER 0x06
#define DHCP_LOG_SERVER 0x07
#define DHCP_COOKIE_SERVER 0x08
#define DHCP_LPR_SERVER 0x09
#define DHCP_HOST_NAME 0x0c
#define DHCP_BOOT_SIZE 0x0d
#define DHCP_DOMAIN_NAME 0x0f
#define DHCP_SWAP_SERVER 0x10
#define DHCP_ROOT_PATH 0x11
#define DHCP_IP_TTL 0x17
#define DHCP_MTU 0x1a
#define DHCP_BROADCAST 0x1c
#define DHCP_NTP_SERVER 0x2a
#define DHCP_WINS_SERVER 0x2c
#define DHCP_REQUESTED_IP 0x32
#define DHCP_LEASE_TIME 0x33
#define DHCP_OPTION_OVER 0x34
#define DHCP_MESSAGE_TYPE 0x35
#define DHCP_SERVER_ID 0x36
#define DHCP_PARAM_REQ 0x37
#define DHCP_MESSAGE 0x38
#define DHCP_MAX_SIZE 0x39
#define DHCP_T1 0x3a
#define DHCP_T2 0x3b
#define DHCP_VENDOR 0x3c
#define DHCP_CLIENT_ID 0x3d
#define DHCP_FQDN 0x51
#define DHCP_END 0xFF
#define BOOTREQUEST 1
#define BOOTREPLY 2
#define ETH_10MB 1
#define ETH_10MB_LEN 6
#define DHCPDISCOVER 1
#define DHCPOFFER 2
#define DHCPREQUEST 3
#define DHCPDECLINE 4
#define DHCPACK 5
#define DHCPNAK 6
#define DHCPRELEASE 7
#define DHCPINFORM 8
#define BROADCAST_FLAG 0x8000
#define OPTION_FIELD 0
#define FILE_FIELD 1
#define SNAME_FIELD 2
/* miscellaneous defines */
#define MAC_BCAST_ADDR (uint8_t *) "\xff\xff\xff\xff\xff\xff"
#define OPT_CODE 0
#define OPT_LEN 1
#define OPT_DATA 2
struct option_set {
uint8_t *data;
struct option_set *next;
};
struct static_lease {
uint8_t *mac;
uint32_t *ip;
struct static_lease *next;
};
struct server_config_t {
uint32_t server; /* Our IP, in network order */
uint32_t start; /* Start address of leases, network order */
uint32_t end; /* End of leases, network order */
struct option_set *options; /* List of DHCP options loaded from the config file */
char *interface; /* The name of the interface to use */
int ifindex; /* Index number of the interface to use */
uint8_t arp[6]; /* Our arp address */
unsigned long lease; /* lease time in seconds (host order) */
unsigned long max_leases; /* maximum number of leases (including reserved address) */
char remaining; /* should the lease file be interpreted as lease time remaining, or
* as the time the lease expires */
unsigned long auto_time; /* how long should udhcpd wait before writing a config file.
* if this is zero, it will only write one on SIGUSR1 */
unsigned long decline_time; /* how long an address is reserved if a client returns a
* decline message */
unsigned long conflict_time; /* how long an arp conflict offender is leased for */
unsigned long offer_time; /* how long an offered address is reserved */
unsigned long min_lease; /* minimum lease a client can request*/
char *lease_file;
char *pidfile;
char *notify_file; /* What to run whenever leases are written */
uint32_t siaddr; /* next server bootp option */
char *sname; /* bootp server name */
char *boot_file; /* bootp boot file option */
struct static_lease *static_leases; /* List of ip/mac pairs to assign static leases */
};
extern struct server_config_t server_config;
extern struct dhcpOfferedAddr *leases;
#endif
-17
View File
@@ -1,17 +0,0 @@
/* files.h */
#ifndef _FILES_H
#define _FILES_H
struct config_keyword {
const char *keyword;
int (* const handler)(const char *line, void *var);
void *var;
const char *def;
};
int read_config(const char *file);
void write_leases(void);
void read_leases(const char *file);
#endif
-23
View File
@@ -1,23 +0,0 @@
/* leases.h */
#ifndef _LEASES_H
#define _LEASES_H
struct dhcpOfferedAddr {
uint8_t chaddr[16];
uint32_t yiaddr; /* network order */
uint32_t expires; /* host order */
};
extern uint8_t blank_chaddr[];
void clear_lease(uint8_t *chaddr, uint32_t yiaddr);
struct dhcpOfferedAddr *add_lease(uint8_t *chaddr, uint32_t yiaddr, unsigned long lease);
int lease_expired(struct dhcpOfferedAddr *lease);
struct dhcpOfferedAddr *oldest_expired_lease(void);
struct dhcpOfferedAddr *find_lease_by_chaddr(uint8_t *chaddr);
struct dhcpOfferedAddr *find_lease_by_yiaddr(uint32_t yiaddr);
uint32_t find_address(int check_expired);
#endif
-54
View File
@@ -1,54 +0,0 @@
/* libbb_udhcp.h - busybox compatability wrapper */
/* bit of a hack, do this no matter what the order of the includes.
* (for busybox) */
#ifdef CONFIG_INSTALL_NO_USR
#undef DEFAULT_SCRIPT
#define DEFAULT_SCRIPT "/share/udhcpc/default.script"
#endif
#ifndef _LIBBB_UDHCP_H
#define _LIBBB_UDHCP_H
#ifdef IN_BUSYBOX
#include "busybox.h"
#ifdef CONFIG_FEATURE_UDHCP_SYSLOG
#define UDHCP_SYSLOG
#endif
#ifdef CONFIG_FEATURE_UDHCP_DEBUG
#define UDHCP_DEBUG
#endif
#define COMBINED_BINARY
#include "version.h"
#define xfopen bb_xfopen
#else /* ! BB_VER */
#include <stdlib.h>
#include <stdio.h>
#include <sys/sysinfo.h>
#define TRUE 1
#define FALSE 0
#define xmalloc malloc
#define xcalloc calloc
static inline FILE *xfopen(const char *file, const char *mode)
{
FILE *fp;
if (!(fp = fopen(file, mode))) {
perror("could not open input file");
exit(0);
}
return fp;
}
#endif /* BB_VER */
#endif /* _LIBBB_UDHCP_H */
-234
View File
@@ -1,234 +0,0 @@
/*
* options.c -- DHCP server option packet tools
* Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
*/
#include <stdlib.h>
#include <string.h>
#include "dhcpd.h"
#include "files.h"
#include "options.h"
#include "common.h"
/* supported options are easily added here */
struct dhcp_option dhcp_options[] = {
/* name[10] flags code */
{"subnet", OPTION_IP | OPTION_REQ, 0x01},
{"timezone", OPTION_S32, 0x02},
{"router", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x03},
{"timesvr", OPTION_IP | OPTION_LIST, 0x04},
{"namesvr", OPTION_IP | OPTION_LIST, 0x05},
{"dns", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x06},
{"logsvr", OPTION_IP | OPTION_LIST, 0x07},
{"cookiesvr", OPTION_IP | OPTION_LIST, 0x08},
{"lprsvr", OPTION_IP | OPTION_LIST, 0x09},
{"hostname", OPTION_STRING | OPTION_REQ, 0x0c},
{"bootsize", OPTION_U16, 0x0d},
{"domain", OPTION_STRING | OPTION_REQ, 0x0f},
{"swapsvr", OPTION_IP, 0x10},
//We had the possibility for the client to ask for a rootpath
//closes http://bugs.debian.org/279110 (ericvb@debian.org)
//{"rootpath", OPTION_STRING, 0x11},
{"rootpath", OPTION_STRING | OPTION_REQ, 0x11},
{"ipttl", OPTION_U8, 0x17},
{"mtu", OPTION_U16, 0x1a},
{"broadcast", OPTION_IP | OPTION_REQ, 0x1c},
{"nisdomain", OPTION_STRING | OPTION_REQ, 0x28},
{"nissrv", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x29},
{"ntpsrv", OPTION_IP | OPTION_LIST | OPTION_REQ, 0x2a},
{"wins", OPTION_IP | OPTION_LIST, 0x2c},
{"requestip", OPTION_IP, 0x32},
{"lease", OPTION_U32, 0x33},
{"dhcptype", OPTION_U8, 0x35},
{"serverid", OPTION_IP, 0x36},
{"message", OPTION_STRING, 0x38},
{"tftp", OPTION_STRING, 0x42},
{"bootfile", OPTION_STRING, 0x43},
{"wpad", OPTION_STRING, 0xfc},
{"", 0x00, 0x00}
};
/* Lengths of the different option types */
int option_lengths[] = {
[OPTION_IP] = 4,
[OPTION_IP_PAIR] = 8,
[OPTION_BOOLEAN] = 1,
[OPTION_STRING] = 1,
[OPTION_U8] = 1,
[OPTION_U16] = 2,
[OPTION_S16] = 2,
[OPTION_U32] = 4,
[OPTION_S32] = 4
};
/* get an option with bounds checking (warning, not aligned). */
uint8_t *get_option(struct dhcpMessage *packet, int code)
{
int i, length;
uint8_t *optionptr;
int over = 0, done = 0, curr = OPTION_FIELD;
optionptr = packet->options;
i = 0;
length = 308;
while (!done) {
if (i >= length) {
LOG(LOG_WARNING, "bogus packet, option fields too long.");
return NULL;
}
if (optionptr[i + OPT_CODE] == code) {
if (i + 1 + optionptr[i + OPT_LEN] >= length) {
LOG(LOG_WARNING, "bogus packet, option fields too long.");
return NULL;
}
return optionptr + i + 2;
}
switch (optionptr[i + OPT_CODE]) {
case DHCP_PADDING:
i++;
break;
case DHCP_OPTION_OVER:
if (i + 1 + optionptr[i + OPT_LEN] >= length) {
LOG(LOG_WARNING, "bogus packet, option fields too long.");
return NULL;
}
over = optionptr[i + 3];
i += optionptr[OPT_LEN] + 2;
break;
case DHCP_END:
if (curr == OPTION_FIELD && over & FILE_FIELD) {
optionptr = packet->file;
i = 0;
length = 128;
curr = FILE_FIELD;
} else if (curr == FILE_FIELD && over & SNAME_FIELD) {
optionptr = packet->sname;
i = 0;
length = 64;
curr = SNAME_FIELD;
} else done = 1;
break;
default:
i += optionptr[OPT_LEN + i] + 2;
}
}
return NULL;
}
/* return the position of the 'end' option (no bounds checking) */
int end_option(uint8_t *optionptr)
{
int i = 0;
while (optionptr[i] != DHCP_END) {
if (optionptr[i] == DHCP_PADDING) i++;
else i += optionptr[i + OPT_LEN] + 2;
}
return i;
}
/* add an option string to the options (an option string contains an option code,
* length, then data) */
int add_option_string(uint8_t *optionptr, uint8_t *string)
{
int end = end_option(optionptr);
/* end position + string length + option code/length + end option */
if (end + string[OPT_LEN] + 2 + 1 >= 308) {
LOG(LOG_ERR, "Option 0x%02x did not fit into the packet!", string[OPT_CODE]);
return 0;
}
DEBUG(LOG_INFO, "adding option 0x%02x", string[OPT_CODE]);
memcpy(optionptr + end, string, string[OPT_LEN] + 2);
optionptr[end + string[OPT_LEN] + 2] = DHCP_END;
return string[OPT_LEN] + 2;
}
/* add a one to four byte option to a packet */
int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data)
{
char length = 0;
int i;
uint8_t option[2 + 4];
uint8_t *u8;
uint16_t *u16;
uint32_t *u32;
uint32_t aligned;
u8 = (uint8_t *) &aligned;
u16 = (uint16_t *) &aligned;
u32 = &aligned;
for (i = 0; dhcp_options[i].code; i++)
if (dhcp_options[i].code == code) {
length = option_lengths[dhcp_options[i].flags & TYPE_MASK];
}
if (!length) {
DEBUG(LOG_ERR, "Could not add option 0x%02x", code);
return 0;
}
option[OPT_CODE] = code;
option[OPT_LEN] = length;
switch (length) {
case 1: *u8 = data; break;
case 2: *u16 = data; break;
case 4: *u32 = data; break;
}
memcpy(option + 2, &aligned, length);
return add_option_string(optionptr, option);
}
/* find option 'code' in opt_list */
struct option_set *find_option(struct option_set *opt_list, char code)
{
while (opt_list && opt_list->data[OPT_CODE] < code)
opt_list = opt_list->next;
if (opt_list && opt_list->data[OPT_CODE] == code) return opt_list;
else return NULL;
}
/* add an option to the opt_list */
void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length)
{
struct option_set *existing, *new, **curr;
/* add it to an existing option */
if ((existing = find_option(*opt_list, option->code))) {
DEBUG(LOG_INFO, "Attaching option %s to existing member of list", option->name);
if (option->flags & OPTION_LIST) {
if (existing->data[OPT_LEN] + length <= 255) {
existing->data = realloc(existing->data,
existing->data[OPT_LEN] + length + 2);
memcpy(existing->data + existing->data[OPT_LEN] + 2, buffer, length);
existing->data[OPT_LEN] += length;
} /* else, ignore the data, we could put this in a second option in the future */
} /* else, ignore the new data */
} else {
DEBUG(LOG_INFO, "Attaching option %s to list", option->name);
/* make a new option */
new = xmalloc(sizeof(struct option_set));
new->data = xmalloc(length + 2);
new->data[OPT_CODE] = option->code;
new->data[OPT_LEN] = length;
memcpy(new->data + 2, buffer, length);
curr = opt_list;
while (*curr && (*curr)->data[OPT_CODE] < option->code)
curr = &(*curr)->next;
new->next = *curr;
*curr = new;
}
}
-40
View File
@@ -1,40 +0,0 @@
/* options.h */
#ifndef _OPTIONS_H
#define _OPTIONS_H
#include "packet.h"
#define TYPE_MASK 0x0F
enum {
OPTION_IP=1,
OPTION_IP_PAIR,
OPTION_STRING,
OPTION_BOOLEAN,
OPTION_U8,
OPTION_U16,
OPTION_S16,
OPTION_U32,
OPTION_S32
};
#define OPTION_REQ 0x10 /* have the client request this option */
#define OPTION_LIST 0x20 /* There can be a list of 1 or more of these */
struct dhcp_option {
char name[10];
char flags;
uint8_t code;
};
extern struct dhcp_option dhcp_options[];
extern int option_lengths[];
uint8_t *get_option(struct dhcpMessage *packet, int code);
int end_option(uint8_t *optionptr);
int add_option_string(uint8_t *optionptr, uint8_t *string);
int add_simple_option(uint8_t *optionptr, uint8_t code, uint32_t data);
struct option_set *find_option(struct option_set *opt_list, char code);
void attach_option(struct option_set **opt_list, struct dhcp_option *option, char *buffer, int length);
#endif
-211
View File
@@ -1,211 +0,0 @@
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <features.h>
#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
#include <netpacket/packet.h>
#include <net/ethernet.h>
#else
#include <asm/types.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#endif
#include <errno.h>
#include "packet.h"
#include "dhcpd.h"
#include "options.h"
#include "common.h"
void init_header(struct dhcpMessage *packet, char type)
{
memset(packet, 0, sizeof(struct dhcpMessage));
switch (type) {
case DHCPDISCOVER:
case DHCPREQUEST:
case DHCPRELEASE:
case DHCPINFORM:
packet->op = BOOTREQUEST;
break;
case DHCPOFFER:
case DHCPACK:
case DHCPNAK:
packet->op = BOOTREPLY;
}
packet->htype = ETH_10MB;
packet->hlen = ETH_10MB_LEN;
packet->cookie = htonl(DHCP_MAGIC);
packet->options[0] = DHCP_END;
add_simple_option(packet->options, DHCP_MESSAGE_TYPE, type);
}
/* read a packet from socket fd, return -1 on read error, -2 on packet error */
int get_packet(struct dhcpMessage *packet, int fd)
{
int bytes;
int i;
const char broken_vendors[][8] = {
"MSFT 98",
""
};
char unsigned *vendor;
memset(packet, 0, sizeof(struct dhcpMessage));
bytes = read(fd, packet, sizeof(struct dhcpMessage));
if (bytes < 0) {
DEBUG(LOG_INFO, "couldn't read on listening socket, ignoring");
return -1;
}
if (ntohl(packet->cookie) != DHCP_MAGIC) {
LOG(LOG_ERR, "received bogus message, ignoring");
return -2;
}
DEBUG(LOG_INFO, "Received a packet");
if (packet->op == BOOTREQUEST && (vendor = get_option(packet, DHCP_VENDOR))) {
for (i = 0; broken_vendors[i][0]; i++) {
if (vendor[OPT_LEN - 2] == (uint8_t) strlen(broken_vendors[i]) &&
!strncmp(vendor, broken_vendors[i], vendor[OPT_LEN - 2])) {
DEBUG(LOG_INFO, "broken client (%s), forcing broadcast",
broken_vendors[i]);
packet->flags |= htons(BROADCAST_FLAG);
}
}
}
return bytes;
}
uint16_t checksum(void *addr, int count)
{
/* Compute Internet Checksum for "count" bytes
* beginning at location "addr".
*/
register int32_t sum = 0;
uint16_t *source = (uint16_t *) addr;
while (count > 1) {
/* This is the inner loop */
sum += *source++;
count -= 2;
}
/* Add left-over byte, if any */
if (count > 0) {
/* Make sure that the left-over byte is added correctly both
* with little and big endian hosts */
uint16_t tmp = 0;
*(uint8_t *) (&tmp) = * (uint8_t *) source;
sum += tmp;
}
/* Fold 32-bit sum to 16 bits */
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
return ~sum;
}
/* Construct a ip/udp header for a packet, and specify the source and dest hardware address */
int raw_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port,
uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex)
{
int fd;
int result;
struct sockaddr_ll dest;
struct udp_dhcp_packet packet;
if ((fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
DEBUG(LOG_ERR, "socket call failed: %m");
return -1;
}
memset(&dest, 0, sizeof(dest));
memset(&packet, 0, sizeof(packet));
dest.sll_family = AF_PACKET;
dest.sll_protocol = htons(ETH_P_IP);
dest.sll_ifindex = ifindex;
dest.sll_halen = 6;
memcpy(dest.sll_addr, dest_arp, 6);
if (bind(fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_ll)) < 0) {
DEBUG(LOG_ERR, "bind call failed: %m");
close(fd);
return -1;
}
packet.ip.protocol = IPPROTO_UDP;
packet.ip.saddr = source_ip;
packet.ip.daddr = dest_ip;
packet.udp.source = htons(source_port);
packet.udp.dest = htons(dest_port);
packet.udp.len = htons(sizeof(packet.udp) + sizeof(struct dhcpMessage)); /* cheat on the psuedo-header */
packet.ip.tot_len = packet.udp.len;
memcpy(&(packet.data), payload, sizeof(struct dhcpMessage));
packet.udp.check = checksum(&packet, sizeof(struct udp_dhcp_packet));
packet.ip.tot_len = htons(sizeof(struct udp_dhcp_packet));
packet.ip.ihl = sizeof(packet.ip) >> 2;
packet.ip.version = IPVERSION;
packet.ip.ttl = IPDEFTTL;
packet.ip.check = checksum(&(packet.ip), sizeof(packet.ip));
result = sendto(fd, &packet, sizeof(struct udp_dhcp_packet), 0, (struct sockaddr *) &dest, sizeof(dest));
if (result <= 0) {
DEBUG(LOG_ERR, "write on socket failed: %m");
}
close(fd);
return result;
}
/* Let the kernel do all the work for packet generation */
int kernel_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port,
uint32_t dest_ip, int dest_port)
{
int n = 1;
int fd, result;
struct sockaddr_in client;
if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
return -1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1)
{
close(fd);
return -1;
}
memset(&client, 0, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(source_port);
client.sin_addr.s_addr = source_ip;
if (bind(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1)
{
close(fd);
return -1;
}
memset(&client, 0, sizeof(client));
client.sin_family = AF_INET;
client.sin_port = htons(dest_port);
client.sin_addr.s_addr = dest_ip;
if (connect(fd, (struct sockaddr *)&client, sizeof(struct sockaddr)) == -1)
{
close(fd);
return -1;
}
result = write(fd, payload, sizeof(struct dhcpMessage));
close(fd);
return result;
}
-41
View File
@@ -1,41 +0,0 @@
#ifndef _PACKET_H
#define _PACKET_H
#include <netinet/udp.h>
#include <netinet/ip.h>
struct dhcpMessage {
uint8_t op;
uint8_t htype;
uint8_t hlen;
uint8_t hops;
uint32_t xid;
uint16_t secs;
uint16_t flags;
uint32_t ciaddr;
uint32_t yiaddr;
uint32_t siaddr;
uint32_t giaddr;
uint8_t chaddr[16];
uint8_t sname[64];
uint8_t file[128];
uint32_t cookie;
uint8_t options[308]; /* 312 - cookie */
};
struct udp_dhcp_packet {
struct iphdr ip;
struct udphdr udp;
struct dhcpMessage data;
};
void init_header(struct dhcpMessage *packet, char type);
int get_packet(struct dhcpMessage *packet, int fd);
uint16_t checksum(void *addr, int count);
int raw_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port,
uint32_t dest_ip, int dest_port, uint8_t *dest_arp, int ifindex);
int kernel_packet(struct dhcpMessage *payload, uint32_t source_ip, int source_port,
uint32_t dest_ip, int dest_port);
#endif
-75
View File
@@ -1,75 +0,0 @@
/* pidfile.c
*
* Functions to assist in the writing and removing of pidfiles.
*
* Russ Dill <Russ.Dill@asu.edu> September 2001
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include "pidfile.h"
#include "common.h"
static char *saved_pidfile;
static void pidfile_delete(void)
{
if (saved_pidfile) unlink(saved_pidfile);
}
int pidfile_acquire(const char *pidfile)
{
int pid_fd;
if (!pidfile) return -1;
pid_fd = open(pidfile, O_CREAT | O_WRONLY, 0644);
if (pid_fd < 0) {
LOG(LOG_ERR, "Unable to open pidfile %s: %m\n", pidfile);
} else {
lockf(pid_fd, F_LOCK, 0);
if (!saved_pidfile)
atexit(pidfile_delete);
saved_pidfile = (char *) pidfile;
}
return pid_fd;
}
void pidfile_write_release(int pid_fd)
{
FILE *out;
if (pid_fd < 0) return;
if ((out = fdopen(pid_fd, "w")) != NULL) {
fprintf(out, "%d\n", getpid());
fclose(out);
}
lockf(pid_fd, F_UNLCK, 0);
close(pid_fd);
}
-25
View File
@@ -1,25 +0,0 @@
/* pidfile.h
*
* Functions to assist in the writing and removing of pidfiles.
*
* Russ Dill <Russ.Dill@asu.edu> September 2001
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
int pidfile_acquire(const char *pidfile);
void pidfile_write_release(int pid_fd);
-242
View File
@@ -1,242 +0,0 @@
/* script.c
*
* Functions to call the DHCP client notification scripts
*
* Russ Dill <Russ.Dill@asu.edu> July 2001
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "options.h"
#include "dhcpd.h"
#include "dhcpc.h"
#include "common.h"
/* get a rough idea of how long an option will be (rounding up...) */
static const int max_option_length[] = {
[OPTION_IP] = sizeof("255.255.255.255 "),
[OPTION_IP_PAIR] = sizeof("255.255.255.255 ") * 2,
[OPTION_STRING] = 1,
[OPTION_BOOLEAN] = sizeof("yes "),
[OPTION_U8] = sizeof("255 "),
[OPTION_U16] = sizeof("65535 "),
[OPTION_S16] = sizeof("-32768 "),
[OPTION_U32] = sizeof("4294967295 "),
[OPTION_S32] = sizeof("-2147483684 "),
};
static inline int upper_length(int length, int opt_index)
{
return max_option_length[opt_index] *
(length / option_lengths[opt_index]);
}
static int sprintip(char *dest, char *pre, uint8_t *ip)
{
return sprintf(dest, "%s%d.%d.%d.%d", pre, ip[0], ip[1], ip[2], ip[3]);
}
/* really simple implementation, just count the bits */
static int mton(struct in_addr *mask)
{
int i;
unsigned long bits = ntohl(mask->s_addr);
/* too bad one can't check the carry bit, etc in c bit
* shifting */
for (i = 0; i < 32 && !((bits >> i) & 1); i++);
return 32 - i;
}
/* Fill dest with the text of option 'option'. */
static void fill_options(char *dest, uint8_t *option, struct dhcp_option *type_p)
{
int type, optlen;
uint16_t val_u16;
int16_t val_s16;
uint32_t val_u32;
int32_t val_s32;
int len = option[OPT_LEN - 2];
dest += sprintf(dest, "%s=", type_p->name);
type = type_p->flags & TYPE_MASK;
optlen = option_lengths[type];
for(;;) {
switch (type) {
case OPTION_IP_PAIR:
dest += sprintip(dest, "", option);
*(dest++) = '/';
option += 4;
optlen = 4;
case OPTION_IP: /* Works regardless of host byte order. */
dest += sprintip(dest, "", option);
break;
case OPTION_BOOLEAN:
dest += sprintf(dest, *option ? "yes" : "no");
break;
case OPTION_U8:
dest += sprintf(dest, "%u", *option);
break;
case OPTION_U16:
memcpy(&val_u16, option, 2);
dest += sprintf(dest, "%u", ntohs(val_u16));
break;
case OPTION_S16:
memcpy(&val_s16, option, 2);
dest += sprintf(dest, "%d", ntohs(val_s16));
break;
case OPTION_U32:
memcpy(&val_u32, option, 4);
dest += sprintf(dest, "%lu", (unsigned long) ntohl(val_u32));
break;
case OPTION_S32:
memcpy(&val_s32, option, 4);
dest += sprintf(dest, "%ld", (long) ntohl(val_s32));
break;
case OPTION_STRING:
memcpy(dest, option, len);
dest[len] = '\0';
return; /* Short circuit this case */
}
option += optlen;
len -= optlen;
if (len <= 0) break;
dest += sprintf(dest, " ");
}
}
/* put all the parameters into an environment */
static char **fill_envp(struct dhcpMessage *packet)
{
int num_options = 0;
int i, j;
char **envp;
uint8_t *temp;
struct in_addr subnet;
char over = 0;
if (packet == NULL)
num_options = 0;
else {
for (i = 0; dhcp_options[i].code; i++)
if (get_option(packet, dhcp_options[i].code)) {
num_options++;
if (dhcp_options[i].code == DHCP_SUBNET)
num_options++; /* for mton */
}
if (packet->siaddr) num_options++;
if ((temp = get_option(packet, DHCP_OPTION_OVER)))
over = *temp;
if (!(over & FILE_FIELD) && packet->file[0]) num_options++;
if (!(over & SNAME_FIELD) && packet->sname[0]) num_options++;
}
envp = xcalloc(sizeof(char *), num_options + 5);
j = 0;
asprintf(&envp[j++], "interface=%s", client_config.interface);
asprintf(&envp[j++], "%s=%s", "PATH",
getenv("PATH") ? : "/bin:/usr/bin:/sbin:/usr/sbin");
asprintf(&envp[j++], "%s=%s", "HOME", getenv("HOME") ? : "/");
if (packet == NULL) return envp;
envp[j] = xmalloc(sizeof("ip=255.255.255.255"));
sprintip(envp[j++], "ip=", (uint8_t *) &packet->yiaddr);
for (i = 0; dhcp_options[i].code; i++) {
if (!(temp = get_option(packet, dhcp_options[i].code)))
continue;
envp[j] = xmalloc(upper_length(temp[OPT_LEN - 2],
dhcp_options[i].flags & TYPE_MASK) + strlen(dhcp_options[i].name) + 2);
fill_options(envp[j++], temp, &dhcp_options[i]);
/* Fill in a subnet bits option for things like /24 */
if (dhcp_options[i].code == DHCP_SUBNET) {
memcpy(&subnet, temp, 4);
asprintf(&envp[j++], "mask=%d", mton(&subnet));
}
}
if (packet->siaddr) {
envp[j] = xmalloc(sizeof("siaddr=255.255.255.255"));
sprintip(envp[j++], "siaddr=", (uint8_t *) &packet->siaddr);
}
if (!(over & FILE_FIELD) && packet->file[0]) {
/* watch out for invalid packets */
packet->file[sizeof(packet->file) - 1] = '\0';
asprintf(&envp[j++], "boot_file=%s", packet->file);
}
if (!(over & SNAME_FIELD) && packet->sname[0]) {
/* watch out for invalid packets */
packet->sname[sizeof(packet->sname) - 1] = '\0';
asprintf(&envp[j++], "sname=%s", packet->sname);
}
return envp;
}
/* Call a script with a par file and env vars */
void run_script(struct dhcpMessage *packet, const char *name)
{
int pid;
char **envp, **curr;
if (client_config.callback == NULL)
return;
DEBUG(LOG_INFO, "callbacking'ing %p", client_config.callback);
envp = fill_envp(packet);
/* call script */
// pid = vfork();
// if (pid) {
// waitpid(pid, NULL, 0);
// for (curr = envp; *curr; curr++) free(*curr);
// free(envp);
// return;
// } else if (pid == 0) {
// /* close fd's? */
// /* exec script */
// execle(client_config.script, client_config.script,
// name, NULL, envp);
// LOG(LOG_ERR, "script %s failed: %m", client_config.script);
// exit(1);
// }
client_config.callback(name, envp, client_config.callback_data);
// Free strings
char **e = envp;
while (*e) {
free(*e);
e++;
}
free(envp);
}
-6
View File
@@ -1,6 +0,0 @@
#ifndef _SCRIPT_H
#define _SCRIPT_H
void run_script(struct dhcpMessage *packet, const char *name);
#endif
-78
View File
@@ -1,78 +0,0 @@
/* signalpipe.c
*
* Signal pipe infrastructure. A reliable way of delivering signals.
*
* Russ Dill <Russ.Dill@asu.edu> December 2003
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include "signalpipe.h"
#include "common.h"
static int signal_pipe[2];
static void signal_handler(int sig)
{
if (send(signal_pipe[1], &sig, sizeof(sig), MSG_DONTWAIT) < 0)
DEBUG(LOG_ERR, "Could not send signal: %m");
}
/* Call this before doing anything else. Sets up the socket pair
* and installs the signal handler */
void udhcp_sp_setup(void)
{
socketpair(AF_UNIX, SOCK_STREAM, 0, signal_pipe);
signal(SIGUSR1, signal_handler);
signal(SIGUSR2, signal_handler);
signal(SIGTERM, signal_handler);
}
/* Quick little function to setup the rfds. Will return the
* max_fd for use with select. Limited in that you can only pass
* one extra fd */
int udhcp_sp_fd_set(fd_set *rfds, int extra_fd)
{
FD_ZERO(rfds);
FD_SET(signal_pipe[0], rfds);
if (extra_fd >= 0) FD_SET(extra_fd, rfds);
return signal_pipe[0] > extra_fd ? signal_pipe[0] : extra_fd;
}
/* Read a signal from the signal pipe. Returns 0 if there is
* no signal, -1 on error (and sets errno appropriately), and
* your signal on success */
int udhcp_sp_read(fd_set *rfds)
{
int sig;
if (!FD_ISSET(signal_pipe[0], rfds))
return 0;
if (read(signal_pipe[0], &sig, sizeof(sig)) < 0)
return -1;
return sig;
}
-22
View File
@@ -1,22 +0,0 @@
/* signalpipe.h
*
* Russ Dill <Russ.Dill@asu.edu> December 2003
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
void udhcp_sp_setup(void);
int udhcp_sp_fd_set(fd_set *rfds, int extra_fd);
int udhcp_sp_read(fd_set *rfds);
-137
View File
@@ -1,137 +0,0 @@
/*
* socket.c -- DHCP server client/server socket creation
*
* udhcp client/server
* Copyright (C) 1999 Matthew Ramsay <matthewr@moreton.com.au>
* Chris Trew <ctrew@moreton.com.au>
*
* Rewrite by Russ Dill <Russ.Dill@asu.edu> July 2001
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <errno.h>
#include <features.h>
#if __GLIBC__ >=2 && __GLIBC_MINOR >= 1
#include <netpacket/packet.h>
#include <net/ethernet.h>
#else
#include <asm/types.h>
#include <linux/if_packet.h>
#include <linux/if_ether.h>
#endif
#include "socket.h"
#include "common.h"
int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp)
{
int fd;
struct ifreq ifr;
struct sockaddr_in *our_ip;
memset(&ifr, 0, sizeof(struct ifreq));
if((fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) >= 0) {
ifr.ifr_addr.sa_family = AF_INET;
//closes http://bugs.debian.org/283582
//strcpy(ifr.ifr_name, interface);
strncpy(ifr.ifr_name, interface, IFNAMSIZ-1);
if (addr) {
if (ioctl(fd, SIOCGIFADDR, &ifr) == 0) {
our_ip = (struct sockaddr_in *) &ifr.ifr_addr;
*addr = our_ip->sin_addr.s_addr;
DEBUG(LOG_INFO, "%s (our ip) = %s", ifr.ifr_name, inet_ntoa(our_ip->sin_addr));
} else {
LOG(LOG_ERR, "SIOCGIFADDR failed, is the interface up and configured?: %m");
close(fd);
return -1;
}
}
if (ioctl(fd, SIOCGIFINDEX, &ifr) == 0) {
DEBUG(LOG_INFO, "adapter index %d", ifr.ifr_ifindex);
*ifindex = ifr.ifr_ifindex;
} else {
LOG(LOG_ERR, "SIOCGIFINDEX failed!: %m");
close(fd);
return -1;
}
if (ioctl(fd, SIOCGIFHWADDR, &ifr) == 0) {
memcpy(arp, ifr.ifr_hwaddr.sa_data, 6);
DEBUG(LOG_INFO, "adapter hardware address %02x:%02x:%02x:%02x:%02x:%02x",
arp[0], arp[1], arp[2], arp[3], arp[4], arp[5]);
} else {
LOG(LOG_ERR, "SIOCGIFHWADDR failed!: %m");
close(fd);
return -1;
}
} else {
LOG(LOG_ERR, "socket failed!: %m");
return -1;
}
close(fd);
return 0;
}
int listen_socket(uint32_t ip, int port, char *inf)
{
struct ifreq interface;
int fd;
struct sockaddr_in addr;
int n = 1;
DEBUG(LOG_INFO, "Opening listen socket on 0x%08x:%d %s", ip, port, inf);
if ((fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
DEBUG(LOG_ERR, "socket call failed: %m");
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = ip;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &n, sizeof(n)) == -1) {
close(fd);
return -1;
}
if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char *) &n, sizeof(n)) == -1) {
close(fd);
return -1;
}
strncpy(interface.ifr_ifrn.ifrn_name, inf, IFNAMSIZ);
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,(char *)&interface, sizeof(interface)) < 0) {
close(fd);
return -1;
}
if (bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) {
close(fd);
return -1;
}
return fd;
}
-8
View File
@@ -1,8 +0,0 @@
/* socket.h */
#ifndef _SOCKET_H
#define _SOCKET_H
int read_interface(char *interface, int *ifindex, uint32_t *addr, uint8_t *arp);
int listen_socket(uint32_t ip, int port, char *inf);
#endif
-6
View File
@@ -1,6 +0,0 @@
#ifndef _UDHCP_VERSION_H
#define _UDHCP_VERSION_H
#define VERSION "0.9.9-pre"
#endif
+426 -204
View File
File diff suppressed because it is too large. Load diff
+4 -1
View File
@@ -7,7 +7,8 @@
struct wpa_ctrl;
void wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, char *buf, size_t *buf_len);
int wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, char *buf,
size_t *buf_len);
int start_wpa_supplicant(const char *wireless_interface, const char *config_file, pid_t *pid);
int is_networkmanager_managing_device(const char *wireless_interface, int *is_managed);
@@ -15,6 +16,8 @@ int disable_networkmanager_on_device(const char *wireless_interface);
int enable_networkmanager_on_device(const char *wireless_interface);
void nlprint(const char *fmt, ...);
ssize_t run_process_and_read_stdout(const char **args, char *read_buffer,
size_t read_buffer_len);
void pipe_listen(int local, const char *wireless_interface, const char *log_file);